/****************************************************************************
 *
 * This file is part of the CodeMax editor support code.
 *
 * Primary author: Christopher J. Cason.
 *
 * from Persistence of Vision(tm) Ray Tracer version 3.6.
 * Copyright 1991-2003 Persistence of Vision Team
 * Copyright 2003-2004 Persistence of Vision Raytracer Pty. Ltd.
 *---------------------------------------------------------------------------
 * NOTICE: This source code file is provided so that users may experiment
 * with enhancements to POV-Ray and to port the software to platforms other
 * than those supported by the POV-Ray developers. There are strict rules
 * regarding how you are permitted to use this file. These rules are contained
 * in the distribution and derivative versions licenses which should have been
 * provided with this file.
 *
 * These licences may be found online, linked from the end-user license
 * agreement that is located at http://www.povray.org/povlegal.html
 *---------------------------------------------------------------------------
 * This program is based on the popular DKB raytracer version 2.12.
 * DKBTrace was originally written by David K. Buck.
 * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
 *---------------------------------------------------------------------------
 * Thanks to WinMain Software (http://www.winmain.com/) for providing the (at
 * the time commercial) CodeMax edit control which the POVWIN editor is based.
 *---------------------------------------------------------------------------
 * $File: //depot/misc/win-sourcedist/codemax/EditForm.cpp $
 * $Revision: #1 $
 * $Change: 2944 $
 * $DateTime: 2004/07/05 13:11:12 $
 * $Author: chrisc $
 * $Log$
 *****************************************************************************/

#include <vcl.h>
#pragma hdrstop

#include <assert.h>
#include <sys\stat.h>
#include <shellapi.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#include <stdio.h>
#include <ctype.h>
#include "EditForm.h"
#include "AskSave.h"
#include "SaveFailed.h"
#include "RenderSave.h"
#include "InsertChangeThread.h"
#include "GenericInput.h"
#include "PrintOptions.h"
#include "..\pvedit.h"
#include "POVLangDef.h"
#include "FileChanged.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "TCodeMax"
#pragma link "CTabControl"
#pragma resource "*.dfm"

#define MAX_EDITORS               32
#define MAX_OLDER_FILES           32
#define MAX_KEYWORDS              1024
#define CMD_NEXT_KEYWORD          (CMD_USER_BASE)
#define CMD_PREV_KEYWORD          (CMD_USER_BASE + 1)

#define USE_PROJECT               0

#if USE_PROJECT
#define EDITOR_TAB_OFFSET         1
#define PROJECT_TAB_INDEX         1
#else
#define EDITOR_TAB_OFFSET         0
#define PROJECT_TAB_INDEX         -2
#endif

typedef enum
{
  stSave,
  stSaved = stSave,
  stDiscard,
  stDiscarded = stDiscard,
  stSaveAll,
  stDiscardAll,
  stCancel,
  stRetry,
  stError
} TSaveType ;

typedef struct
{
  TAutoIndent           AutoIndent ;
  bool                  SyntaxHighlighting ;
  bool                  WhiteSpaceDisplay ;
  bool                  TabExpand ;
  bool                  SmoothScrolling ;
  bool                  LineToolTips ;
  bool                  LeftMarginVisible ;
  bool                  CaseSensitive ;
  bool                  PreserveCase ;
  bool                  WholeWordEnabled ;
  bool                  DragDropEnabled ;
  bool                  HSplitterEnabled ;
  bool                  VSplitterEnabled ;
  bool                  ColumnSelEnabled ;
  bool                  RegexpEnabled ;
  bool                  OvertypeCaret ;
  bool                  SelBoundsEnabled ;
  bool                  TabKeywordExpansion ;
  TScrollStyle          ScrollBars ;
  int                   TabSize ;
  int                   UndoLimit ;
  int                   DefaultFlags ;
  CM_COLORS             Colours ;
  CM_FONTSTYLES         FontStyles ;
  HFONT                 HFont ;
  char                  *HotKeys ;
  int                   HotKeyLen ;
  AnsiString            FindMRUList ;
  AnsiString            ReplaceMRUList ;
  char                  *Macros [CM_MAX_MACROS] ;
  int                   MacroLen [CM_MAX_MACROS] ;
} EditConfigStruct ;

class TCPanel : public TPanel
{
  public:
    void __fastcall CreateParams (TCreateParams &Params) ;
    __fastcall TCPanel (TObject *Parent) : TPanel (Parent) {}
} ;

int                     NotifyBase ;
int                     EditorCount ;
int                     LastLine ;
int                     LastCol ;
int                     AutoReload ;
int                     EditDragOffset ;
int                     EditStartDragOffset ;
int                     TabClientTop ;
int                     TabClientWidth ;
int                     TabXBorder ;
int                     TabYBorder ;
int                     EditorTabOffset = EDITOR_TAB_OFFSET ;
int                     KeywordCount ;
bool                    LastOverwrite ;
bool                    UndoAfterSave ;
bool                    CreateBackups ;
bool                    EditPanelDrag ;
bool                    EditDragPanelVisible ;
bool                    SaveDialogActive = false ;
char                    *Keywords [MAX_KEYWORDS] ;
HWND                    MainWindow ;
HWND                    MessageWindow ;
HWND                    NotifyWindow ;
HWND                    StatusWindow ;
HANDLE                  EditorSemaphore ;
HANDLE                  CallerSemaphore ;
HANDLE                  VCLSemaphore ;
HANDLE                  EditorMutex ;
TCPanel                 *EditPanel ;
TCodeMax                *Editors [MAX_EDITORS] ;
TCodeMax                *Editor ;
TMenuItem               *RecentMenuItems [9] ;
TMenuItem               *OlderMenuItems [MAX_OLDER_FILES] ;
AnsiString              HomePath ;
AnsiString              InsertPath ;
AnsiString              POVRayIniPath ;
AnsiString              IncludeFilename ;
AnsiString              CommandLine ;
AnsiString              InitialDir ;
TWndMethod              OldEditParentWndProc ;
TWndMethod              OldTabWndProc ;
TParentForm             *ParentForm ;
TStringList             *RecentFiles ;
TStringList             *OlderFiles ;
EditConfigStruct        ec ;
TImageHintWindow        *CurrentHintWindow ;

extern TAskSaveForm     *AskSaveForm ;
extern TSaveFailedForm  *SaveFailedForm ;
extern TGenericInputForm *GenericInputForm ;

class TLock
{
  public:
    TLock (void) { WaitForSingleObject (EditorMutex, INFINITE) ; }
    ~TLock (void) { ReleaseMutex (EditorMutex) ; }
} ;

//---------------------------------------------------------------------------

extern "C" HWND WINAPI __declspec (dllexport) CreateTabWindow (HWND ParentWindow, HWND StatusWindow, char *HomePath) ;
extern "C" DWORD WINAPI __declspec (dllexport) GetDLLVersion (void) ;
extern "C" void WINAPI __declspec (dllexport) SetWindowPosition (int x, int y, int w, int h) ;
extern "C" void WINAPI __declspec (dllexport) SetMessageWindow (HWND MsgWindow) ;
extern "C" void WINAPI __declspec (dllexport) RestoreState (int RestoreFiles) ;
extern "C" void WINAPI __declspec (dllexport) SaveState (void) ;
extern "C" bool WINAPI __declspec (dllexport) SelectFile (char *FileName) ;
extern "C" bool WINAPI __declspec (dllexport) ShowParseError (char *FileName, char *Message, int Line, int Col) ;
extern "C" bool WINAPI __declspec (dllexport) BrowseFile (bool CreateNewWindow) ;
extern "C" bool WINAPI __declspec (dllexport) LoadFile (char *FileName) ;
extern "C" bool WINAPI __declspec (dllexport) ExternalLoadFile (char *ParamString) ;
extern "C" bool WINAPI __declspec (dllexport) CloseFile (char *FileName) ;
extern "C" bool WINAPI __declspec (dllexport) SaveFile (char *FileName) ;
extern "C" DWORD WINAPI __declspec (dllexport) GetTab (void) ;
extern "C" DWORD WINAPI __declspec (dllexport) GetFlags (void) ;
extern "C" char * WINAPI __declspec (dllexport) GetFilename (void) ;
extern "C" void WINAPI __declspec (dllexport) NextTab (bool Forward) ;
extern "C" bool WINAPI __declspec (dllexport) CanClose (bool AllFiles) ;
extern "C" bool WINAPI __declspec (dllexport) SaveModified (char *FileName) ;
extern "C" void WINAPI __declspec (dllexport) ShowMessages (bool on) ;
extern "C" void WINAPI __declspec (dllexport) DispatchMenuId (DWORD id) ;
extern "C" void WINAPI __declspec (dllexport) SetVisible (bool visible) ;
extern "C" HMENU WINAPI __declspec (dllexport) GetMenuHandle (int which) ;
extern "C" void WINAPI __declspec (dllexport) SetNotifyBase (HWND WindowHandle, int MessageBase) ;
extern "C" void WINAPI __declspec (dllexport) UpdateMenus (HMENU MenuHandle) ;
extern "C" void WINAPI __declspec (dllexport) GetContextHelp (void) ;
extern "C" void WINAPI __declspec (dllexport) SetTabFocus (void) ;
extern "C" bool WINAPI __declspec (dllexport) PassOnMessage (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, DWORD *rVal) ;
extern "C" void WINAPI __declspec (dllexport) SetKeywords (LPCSTR Keywords) ;

//---------------------------------------------------------------------------
#if 0
#define DEF_CLRWINDOW             CLR_INVALID
#define DEF_CLRLEFTMARGIN         RGB (255, 255, 255)
#define DEF_CLRBOOKMARK           CLR_INVALID
#define DEF_CLRBOOKMARKBK         CLR_INVALID
#define DEF_CLRTEXT               RGB (0, 0, 0)
#define DEF_CLRTEXTBK             CLR_INVALID
#define DEF_CLRNUMBER             RGB (0, 128, 128)
#define DEF_CLRNUMBERBK           CLR_INVALID
#define DEF_CLRKEYWORD            RGB (0, 0, 255)
#define DEF_CLRKEYWORDBK          CLR_INVALID
#define DEF_CLROPERATOR           RGB (255, 0, 0)
#define DEF_CLROPERATORBK         CLR_INVALID
#define DEF_CLRSCOPEKEYWORD       RGB (0, 0, 255)
#define DEF_CLRSCOPEKEYWORDBK     CLR_INVALID
#define DEF_CLRCOMMENT            RGB (0, 128, 0)
#define DEF_CLRCOMMENTBK          CLR_INVALID
#define DEF_CLRSTRING             RGB (128, 0, 128)
#define DEF_CLRSTRINGBK           CLR_INVALID
#endif

#define DEF_CLRWINDOW             CLR_INVALID
#define DEF_CLRLEFTMARGIN         RGB (0, 255, 255)
#define DEF_CLRBOOKMARK           CLR_INVALID
#define DEF_CLRBOOKMARKBK         CLR_INVALID
#define DEF_CLRTEXT               RGB (0, 0, 0)
#define DEF_CLRTEXTBK             CLR_INVALID
#define DEF_CLRNUMBER             RGB (0, 128, 128)
#define DEF_CLRNUMBERBK           CLR_INVALID
#define DEF_CLRKEYWORD            RGB (128, 0, 128)
#define DEF_CLRKEYWORDBK          CLR_INVALID
#define DEF_CLROPERATOR           RGB (255, 0, 0)
#define DEF_CLROPERATORBK         CLR_INVALID
#define DEF_CLRSCOPEKEYWORD       RGB (0, 0, 255)
#define DEF_CLRSCOPEKEYWORDBK     CLR_INVALID
#define DEF_CLRCOMMENT            RGB (0, 128, 0)
#define DEF_CLRCOMMENTBK          CLR_INVALID
#define DEF_CLRSTRING             RGB (255, 0, 0)
#define DEF_CLRSTRINGBK           CLR_INVALID
#define DEF_CLRTAGTEXT            RGB (0, 0, 0)
#define DEF_CLRTAGTEXTBK          CLR_INVALID
#define DEF_CLRTAGENT             RGB (255, 0, 0)
#define DEF_CLRTAGENTBK           CLR_INVALID
#define DEF_CLRTAGELEMNAME        RGB (128, 0, 0)
#define DEF_CLRTAGELEMNAMEBK      CLR_INVALID
#define DEF_CLRTAGATTRNAME        RGB (0, 0, 255)
#define DEF_CLRTAGATTRNAMEBK      CLR_INVALID
#define DEF_CLRLINENUMBER         RGB (0, 0, 0)
#define DEF_CLRLINENUMBERBK       RGB (255, 255, 255)
#define DEF_CLRHDIVIDERLINES      CLR_INVALID
#define DEF_CLRVDIVIDERLINES      CLR_INVALID
#define DEF_CLRHIGHLIGHTEDLINE    RGB (255, 255, 0)

#define CJC_CLRWINDOW             RGB (0, 0, 0)
#define CJC_CLRLEFTMARGIN         RGB (128, 0, 0)
#define CJC_CLRBOOKMARK           RGB (255, 255, 0)
#define CJC_CLRBOOKMARKBK         CLR_INVALID
#define CJC_CLRTEXT               RGB (255, 255, 255)
#define CJC_CLRTEXTBK             CLR_INVALID
#define CJC_CLRNUMBER             RGB (0, 255, 0)
#define CJC_CLRNUMBERBK           CLR_INVALID
#define CJC_CLRKEYWORD            RGB (0, 255, 255)
#define CJC_CLRKEYWORDBK          CLR_INVALID
#define CJC_CLROPERATOR           RGB (0, 255, 255)
#define CJC_CLROPERATORBK         CLR_INVALID
#define CJC_CLRSCOPEKEYWORD       RGB (0, 255, 0)
#define CJC_CLRSCOPEKEYWORDBK     CLR_INVALID
#define CJC_CLRCOMMENT            RGB (255, 255, 0)
#define CJC_CLRCOMMENTBK          CLR_INVALID
#define CJC_CLRSTRING             RGB (255, 255, 0)
#define CJC_CLRSTRINGBK           CLR_INVALID
#define CJC_CLRTAGTEXT            RGB (0, 255, 255)
#define CJC_CLRTAGTEXTBK          CLR_INVALID
#define CJC_CLRTAGENT             RGB (255, 0, 0)
#define CJC_CLRTAGENTBK           CLR_INVALID
#define CJC_CLRTAGELEMNAME        RGB (255, 255, 0)
#define CJC_CLRTAGELEMNAMEBK      CLR_INVALID
#define CJC_CLRTAGATTRNAME        RGB (0, 255, 255)
#define CJC_CLRTAGATTRNAMEBK      CLR_INVALID
#define CJC_CLRLINENUMBER         RGB (255, 255, 255)
#define CJC_CLRLINENUMBERBK       RGB (0, 0, 0)
#define CJC_CLRHDIVIDERLINES      CLR_INVALID
#define CJC_CLRVDIVIDERLINES      CLR_INVALID
#define CJC_CLRHIGHLIGHTEDLINE    RGB (0, 0, 255)

#define DEF_FSKEYWORD             CM_FONT_NORMAL
#define DEF_FSCOMMENT             CM_FONT_NORMAL
#define DEF_FSOPERATOR            CM_FONT_NORMAL
#define DEF_FSSCOPEKEYWORD        CM_FONT_NORMAL
#define DEF_FSSTRING              CM_FONT_NORMAL
#define DEF_FSTEXT                CM_FONT_NORMAL
#define DEF_FSNUMBER              CM_FONT_NORMAL
#define DEF_FSTAGTEXT             CM_FONT_NORMAL
#define DEF_FSTAGENT              CM_FONT_NORMAL
#define DEF_FSTAGELEMNAME         CM_FONT_NORMAL
#define DEF_FSTAGATTRNAME         CM_FONT_NORMAL
#define DEF_FSLINENUMBER          CM_FONT_NORMAL

CM_COLORS DefaultColours =
{
  DEF_CLRWINDOW,
  DEF_CLRLEFTMARGIN,
  DEF_CLRBOOKMARK,
  DEF_CLRBOOKMARKBK,
  DEF_CLRTEXT,
  DEF_CLRTEXTBK,
  DEF_CLRNUMBER,
  DEF_CLRNUMBERBK,
  DEF_CLRKEYWORD,
  DEF_CLRKEYWORDBK,
  DEF_CLROPERATOR,
  DEF_CLROPERATORBK,
  DEF_CLRSCOPEKEYWORD,
  DEF_CLRSCOPEKEYWORDBK,
  DEF_CLRCOMMENT,
  DEF_CLRCOMMENTBK,
  DEF_CLRSTRING,
  DEF_CLRSTRINGBK,
  DEF_CLRTAGTEXT,
  DEF_CLRTAGTEXTBK,
  DEF_CLRTAGENT,
  DEF_CLRTAGENTBK,
  DEF_CLRTAGELEMNAME,
  DEF_CLRTAGELEMNAMEBK,
  DEF_CLRTAGATTRNAME,
  DEF_CLRTAGATTRNAMEBK,
  DEF_CLRLINENUMBER,
  DEF_CLRLINENUMBERBK,
  DEF_CLRHDIVIDERLINES,
  DEF_CLRVDIVIDERLINES,
  DEF_CLRHIGHLIGHTEDLINE
} ;

CM_COLORS CJCColours =
{
  CJC_CLRWINDOW,
  CJC_CLRLEFTMARGIN,
  CJC_CLRBOOKMARK,
  CJC_CLRBOOKMARKBK,
  CJC_CLRTEXT,
  CJC_CLRTEXTBK,
  CJC_CLRNUMBER,
  CJC_CLRNUMBERBK,
  CJC_CLRKEYWORD,
  CJC_CLRKEYWORDBK,
  CJC_CLROPERATOR,
  CJC_CLROPERATORBK,
  CJC_CLRSCOPEKEYWORD,
  CJC_CLRSCOPEKEYWORDBK,
  CJC_CLRCOMMENT,
  CJC_CLRCOMMENTBK,
  CJC_CLRSTRING,
  CJC_CLRSTRINGBK,
  CJC_CLRTAGTEXT,
  CJC_CLRTAGTEXTBK,
  CJC_CLRTAGENT,
  CJC_CLRTAGENTBK,
  CJC_CLRTAGELEMNAME,
  CJC_CLRTAGELEMNAMEBK,
  CJC_CLRTAGATTRNAME,
  CJC_CLRTAGATTRNAMEBK,
  CJC_CLRLINENUMBER,
  CJC_CLRLINENUMBERBK,
  CJC_CLRHDIVIDERLINES,
  CJC_CLRVDIVIDERLINES,
  CJC_CLRHIGHLIGHTEDLINE
} ;

CM_FONTSTYLES DefaultFontStyles =
{
  DEF_FSTEXT,
  DEF_FSNUMBER,
  DEF_FSKEYWORD,
  DEF_FSOPERATOR,
  DEF_FSSCOPEKEYWORD,
  DEF_FSCOMMENT,
  DEF_FSSTRING,
  DEF_FSTAGTEXT,
  DEF_FSTAGENT,
  DEF_FSTAGELEMNAME,
  DEF_FSTAGATTRNAME,
  DEF_FSLINENUMBER
} ;

//---------------------------------------------------------------------------
void dprintf (char *format, ...)
{
  char                  str [2048] ;
  char                  *s ;
  time_t                t ;
  va_list               arg_ptr ;

  if (strlen (format) > sizeof (str) - 256)
    return ;
  time (&t) ;
  s = ctime (&t) ;
  memcpy (str, s + 11, 9) ;
  va_start (arg_ptr, format) ;
  vsprintf (str + 9, format, arg_ptr) ;
  va_end (arg_ptr) ;
  OutputDebugString (str) ;
}

//---------------------------------------------------------------------------
__fastcall TImageHintWindow::TImageHintWindow (HANDLE Parent, tagRECT &rect, Graphics::TBitmap *bmp) : TCustomControl (Parent)
{
  BoundsRect = rect ;
  if (rect.top + Height > Screen->Height)
    rect.top = Screen->Height - Height ;
  if (rect.left + Width > Screen->Width)
    rect.left = Screen->Width - Width ;
  if (rect.left < 0)
    rect.left = 0 ;
  if (rect.bottom < 0)
    rect.bottom = 0 ;
  bitmap = bmp ;
  SetWindowPos (Handle, HWND_TOPMOST, rect.left, rect.top, 0, 0, SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOSIZE) ;
}

//---------------------------------------------------------------------------
void __fastcall TImageHintWindow::CreateParams (TCreateParams &Params)
{
  TCustomControl::CreateParams (Params) ;
  Params.Style = WS_POPUP | WS_BORDER ;
  Params.WindowClass.style |= CS_SAVEBITS ;
  if (NewStyleControls)
    Params.ExStyle = WS_EX_TOOLWINDOW ;
}

//---------------------------------------------------------------------------
__fastcall TImageHintWindow::~TImageHintWindow ()
{
  if (bitmap != NULL)
    delete bitmap ;
}

//---------------------------------------------------------------------------
bool __fastcall TImageHintWindow::IsHintMessage (int Message)
{
  return ((Message >= WM_KEYFIRST)   && (Message <= WM_KEYLAST))      ||
          ((Message == CM_ACTIVATE)  || (Message == CM_DEACTIVATE))   ||
          (Message == CM_APPKEYDOWN) || (Message == CM_APPSYSCOMMAND) ||
          (Message == WM_COMMAND)    || ((Message > WM_MOUSEMOVE)     &&
          (Message <= WM_MOUSELAST)) || (Message == WM_NCMOUSEMOVE) ;
}

//---------------------------------------------------------------------------
void __fastcall TImageHintWindow::Paint (void)
{
  if (bitmap)
    Canvas->Draw (1, 1, bitmap) ;
}

//---------------------------------------------------------------------------
TRegDef::TRegDef (AnsiString Path) : TRegistry ()
{
  RootKey = HKEY_CURRENT_USER ;
  OpenKey (Path, true) ;
}

int TRegDef::ReadInt (AnsiString Name, int defval)
{
  int                   result ;

  try
  {
    result = TRegistry::ReadInteger (Name) ;
  }
  catch (Exception& e)
  {
    result = defval ;
  }
  return (result) ;
}

bool TRegDef::ReadBool (AnsiString Name, bool defval)
{
  bool                  result ;

  try
  {
    result = TRegistry::ReadBool (Name) ;
  }
  catch (Exception& e)
  {
    result = defval ;
  }
  return (result) ;
}

int TRegDef::ReadBin (AnsiString Name, char *data, int len, char *defval)
{
  int         datasize ;

  try
  {
    datasize = GetDataSize (Name) ;
    if ((len != 0 && len != datasize) || datasize == -1)
    {
      if (defval == NULL)
        return (0) ;
      memcpy (data, defval, len) ;
      return (len) ;
    }
    return (ReadBinaryData (Name, data, datasize)) ;
  }
  catch (Exception& e)
  {
    if (defval != NULL)
    {
      memcpy (data, defval, len) ;
      return (len) ;
    }
    else
      return (0) ;
  }
}

int TRegDef::ReadBin (AnsiString Name, char **data, int len, char *defval)
{
  int         datasize ;

  try
  {
    if (*data != NULL)
      delete [] *data ;
    *data = NULL ;
    datasize = GetDataSize (Name) ;
    if ((len != 0 && len != datasize) || datasize == -1)
    {
      if (defval == NULL)
        return (0) ;
      *data = new char [len] ;
      memcpy (*data, defval, len) ;
      return (len) ;
    }
    *data = new char [datasize] ;
    return (ReadBinaryData (Name, *data, datasize)) ;
  }
  catch (Exception& e)
  {
    if (len == 0)
      return (0) ;
    *data = new char [len] ;
    memcpy (*data, defval, len) ;
    return (len) ;
  }
}

AnsiString TRegDef::ReadString (AnsiString Name, AnsiString defval)
{
  AnsiString            result ;

  result = TRegistry::ReadString (Name) ;
  return (result == "" ? defval : result) ;
}

//---------------------------------------------------------------------------
AnsiString GetNextField (AnsiString& str)
{
  int                   pos = str.Pos (",") ;
  AnsiString            temp ;

  if (pos == 0)
  {
    temp = str ;
    str = "" ;
    return (temp) ;
  }
  temp = str.SubString (1, pos - 1) ;
  str.Delete (1, pos) ;
  return (temp) ;
}

//---------------------------------------------------------------------------
AnsiString GetField (AnsiString str, int FieldNo = 0)
{
  AnsiString            temp = str ;

  while (FieldNo--)
    GetNextField (temp) ;
  return (GetNextField (temp)) ;
}

//---------------------------------------------------------------------------
int FindEditorIndex (AnsiString FileName)
{
  TCodeMax              **e = Editors ;
  EditTagStruct         *t ;

  for (int i = 0 ; i < EditorCount ; i++, e++)
  {
    t = (EditTagStruct *) (*e)->Tag ;
    if (t->LongName.AnsiCompareIC (FileName) == 0)
      return (i) ;
  }
  return (-1) ;
}

//---------------------------------------------------------------------------
TCodeMax *FindEditor (AnsiString FileName)
{
  int         index = FindEditorIndex (FileName) ;

  if (index < 0)
    return (NULL) ;
  return (Editors [index]) ;
}

//---------------------------------------------------------------------------
AnsiString MakeBaseName (AnsiString Name)
{
  return (Name.SubString (Name.LastDelimiter ("\\/:") + 1, MAX_PATH)) ;
}

//---------------------------------------------------------------------------
AnsiString GetFilePath (AnsiString Name)
{
  int                   pos = Name.LastDelimiter ("\\/:") - 1 ;

  return (pos > 0 ? Name.SubString (1, pos) : AnsiString ("")) ;
}

//---------------------------------------------------------------------------
AnsiString GetFileName (AnsiString Name)
{
  int                   index ;
  AnsiString            str = MakeBaseName (Name) ;

  index = str.LastDelimiter (".") ;
  return (index == 0 ? str : str.SubString (1, index - 1)) ;
}

//---------------------------------------------------------------------------
AnsiString GetFileExt (AnsiString Name)
{
  int                   index = Name.LastDelimiter (".") ;

  return (index == 0 ? AnsiString ("") : Name.SubString (index + 1, MAX_PATH)) ;
}

//---------------------------------------------------------------------------
AnsiString GetFullPath (AnsiString Name)
{
  char                  path [MAX_PATH] ;
  char                  *filename ;

  if (GetFullPathName (Name.c_str (), MAX_PATH, path, &filename))
    return (AnsiString (path)) ;
  return (Name) ;
}

//---------------------------------------------------------------------------
AnsiString FixPath (AnsiString Name)
{
  int                   pos ;
  bool                  isUNC = false ;
  AnsiString            result = Name.Trim () ;

  if (result.SubString (1, 2) == "\\\\")
  {
    result.Delete (1, 2) ;
    isUNC = true ;
  }
  while ((pos = result.Pos ("\\\\")) != 0)
    result.Delete (pos, 1) ;
  if (isUNC)
    result.Insert ("\\\\", 1) ;
  return (result) ;
}

//---------------------------------------------------------------------------
AnsiString UnquotePath (AnsiString Name)
{
  AnsiString            result = Name.Trim () ;

  if (result [1] == '"')
    result.Delete (1, 1) ;
  if (result [result.Length ()] == '"')
    result.SetLength (result.Length () - 1) ;
  return (result.Trim ()) ;
}

//---------------------------------------------------------------------------
AnsiString GetBackupPath (AnsiString Name)
{
  return (GetFilePath (Name) + "\\" + GetFileName (Name) + ".bak") ;
}

//---------------------------------------------------------------------------
// simple function to get the FILETIME for the underlying file on disk.  If there is no
// file, then this function will set the time to 'zero'.
void GetFileTimeFromDisk (AnsiString Filename, FILETIME& time)
{
  HANDLE                hFile ;

  time.dwLowDateTime = time.dwHighDateTime = 0 ;
  if (Filename != "")
  {
    hFile = CreateFile (Filename.c_str (), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL) ;
    if (hFile != INVALID_HANDLE_VALUE)
    {
      GetFileTime (hFile, NULL, NULL, &time) ;
      CloseHandle (hFile) ;
    }
  }
}

// updates the internal file time
void UpdateFileTime (TCodeMax *e)
{
  FILETIME              time ;
  EditTagStruct         *t = (EditTagStruct *) e->Tag ;

  GetFileTimeFromDisk (t->LongName, time) ;
  t->TimeSaved = time ;
}

// IsCodeUpToDate() will check to see the file on disk is newer than the window contents.
// If Stale is TRUE, then the code is not up to date if the file on disk is newer.
// If Stale is FALSE, then the code is not up to date if the file on disk is newer or older
bool IsCodeUpToDate (TCodeMax *e, bool Stale)
{
  int                   compare ;
  bool                  upToDate = true ;
  FILETIME              time ;
  EditTagStruct         *t = (EditTagStruct *) e->Tag ;

  if (t->LongName != "")
  {
    GetFileTimeFromDisk (t->LongName, time) ;
    compare = CompareFileTime (&time, &t->TimeSaved) ;
    upToDate = Stale ? (compare <= 0) : (compare == 0) ;
  }
  return (upToDate) ;
}

//---------------------------------------------------------------------------
void CheckReload (void)
{
  bool                  cancel = false ;
  TCodeMax              *e ;
  AnsiString            str ;
  EditTagStruct         *t ;

  if (AutoReload != 0)
  {
    if (AutoReload == 2)
    {
      // always
      for (int i = 0 ; i < EditorCount ; i++)
      {
        e = Editors [i] ;
        t = (EditTagStruct *) e->Tag ;
        if (!IsCodeUpToDate (e, false))
          e->OpenFile (t->LongName) ;
      }
    }
    else
    {
      // ask
      for (int i = 0 ; i < EditorCount ; i++)
      {
        e = Editors [i] ;
        t = (EditTagStruct *) e->Tag ;
        if (!IsCodeUpToDate (e, false))
        {
          if (!cancel)
          {
            switch (FileChangedForm->ShowModal (t->ShortName))
            {
              case mrYes :
                   e->OpenFile (t->LongName) ;
                   break ;

              case mrNo :
                   break ;

              case mrCancel :
                   cancel = true ;
                   break ;
            }
          }
          UpdateFileTime (e) ;
        }
      }
    }
  }
}

//---------------------------------------------------------------------------
void BuildDirList (TMenuItem *Menu, AnsiString Path)
{
  HANDLE                handle ;
  AnsiString            str ;
  AnsiString            name ;
  AnsiString            newpath ;
  TMenuItem             *m ;
  TStringList           *sl ;
  WIN32_FIND_DATA       data ;

  newpath = Path + "\\*.*" ;
  sl = new TStringList ;
  sl->Sorted = true ;
  if ((handle = FindFirstFile (newpath.c_str (), &data)) != INVALID_HANDLE_VALUE)
  {
    do
    {
      if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
      {
        name = data.cFileName ;
        if (name == "." || name == "..")
          continue ;
        sl->Add (name) ;
      }
    } while (FindNextFile (handle, &data)) ;
  }
  FindClose (handle) ;
  for (int i = 0 ; i < sl->Count ; i++)
  {
    m = new TMenuItem (Menu) ;
    str = sl->Strings [i] ;
    if (str.Length () > 5 && str.AnsiPos (" - ") == 3)
      if (isalnum (str [1]) && isalnum (str [2]))
        str.Delete (1, 5) ;
    if (str.SubString (1, 10) != "----------")
    {
      m->Caption = str ;
      BuildDirList (m, Path + "\\" + sl->Strings [i]) ;
      if (m->Count == 0)
        m->Enabled = false ;
    }
    else
      m->Caption = "-" ;
    Menu->Add (m) ;
    if (Menu->Count % 32 == 31)
      m->Break = mbBarBreak ;
  }
  newpath = Path + "\\*.txt" ;
  sl->Clear () ;
  if ((handle = FindFirstFile (newpath.c_str (), &data)) != INVALID_HANDLE_VALUE)
    do
      if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
        sl->Add (data.cFileName) ;
    while (FindNextFile (handle, &data)) ;
  FindClose (handle) ;
  for (int i = 0 ; i < sl->Count ; i++)
  {
    m = new TMenuItem (Menu) ;
    str = GetFileName (sl->Strings [i]) ;
    if (str.Length () > 5 && str.AnsiPos (" - ") == 3)
      if (isalnum (str [1]) && isalnum (str [2]))
        str.Delete (1, 5) ;
    if (str.SubString (1, 10) != "----------")
    {
      m->Caption = str ;
      str = Path + "\\" + GetFileName (sl->Strings [i]) ;
      m->Tag = (int) new AnsiString (str) ;
      str += ".bmp" ;
      if (FileExists (str))
        m->Hint = AnsiString ("Show BMP " + str) ;
      m->OnClick = ParentForm->InsertMenuClick ;
    }
    else
      m->Caption = "-" ;
    Menu->Add (m) ;
    if (Menu->Count % 32 == 31)
      m->Break = mbBarBreak ;
  }
  delete sl ;
}

//---------------------------------------------------------------------------
void PutStatusMessage (char *Message)
{
  SendMessage (StatusWindow, SB_SETTEXT, StatusMessage, (LPARAM) Message) ;
}

//---------------------------------------------------------------------------
void PutStatusMessage (AnsiString Message)
{
  SendMessage (StatusWindow, SB_SETTEXT, StatusMessage, (LPARAM) Message.c_str ()) ;
}

//---------------------------------------------------------------------------
void ShowErrorMessage (AnsiString Title, char *Msg, int ErrorCode = 0)
{
  char                  *buffer ;
  AnsiString            str (Msg) ;

  PutStatusMessage (Msg) ;
  if (ErrorCode == 0)
    ErrorCode = GetLastError () ;
  Title = Title == "" ? AnsiString ("POV-Ray Editor") : AnsiString ("File '") + Title + "'" ;
  FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                 NULL,
                 ErrorCode,
                 MAKELANGID (LANG_ENGLISH, SUBLANG_ENGLISH_US),
                 (char *) &buffer,
                 0,
                 NULL) ;
  str += strlen (buffer) > 45 ? ":\n\n" : ": " ;
  str += buffer ;
  LocalFree (buffer) ;
  MessageBox (MainWindow, str.c_str (), Title.c_str (), MB_OK | MB_ICONEXCLAMATION) ;
}

//---------------------------------------------------------------------------
void MakeFileNames (EditTagStruct *t, AnsiString Name)
{
  char                  path [MAX_PATH] ;
  char                  *filename ;

  if (GetFullPathName (Name.c_str (), MAX_PATH, path, &filename))
  {
    t->LongName = path ;
    t->ShortName = filename ;
  }
  else
  {
    t->LongName = Name ;
    t->ShortName = MakeBaseName (Name) ;
  }
}

//---------------------------------------------------------------------------
void SetSubMenuSelection (TMenuItem *Parent, int value)
{
  int                   count = Parent->Count ;

  for (int i = 0 ; i < count ; i++)
    Parent->Items [i]->Checked = Parent->Items [i]->Tag == value ;
}

//---------------------------------------------------------------------------
void EnableSubmenuItems (TMenuItem *Parent, bool Enabled)
{
  int                   count = Parent->Count ;

  for (int i = 0 ; i < count ; i++)
    Parent->Items [i]->Enabled = Enabled ;
}

//---------------------------------------------------------------------------
void MakeRecentMenus (void)
{
  int                count = RecentFiles->Count ;
  AnsiString         str ;

  for (int i = 0 ; i < 9 ; i++)
  {
    if (i < count)
    {
      str = AnsiString ("&") + (i + 1) + " " + MakeBaseName (GetField (RecentFiles->Strings [i], 0)) ;
      RecentMenuItems [i]->Caption = str ;
      RecentMenuItems [i]->Visible = true ;
      RecentMenuItems [i]->Hint = GetField (RecentFiles->Strings [i], 0) ;
    }
    else
      RecentMenuItems [i]->Visible = false ;
  }
  ParentForm->FileBreak->Visible = count > 0 ;
}

//---------------------------------------------------------------------------
void MakeOlderMenus (void)
{
  int                count = OlderFiles->Count ;
  AnsiString         str ;

  for (int i = 0 ; i < MAX_OLDER_FILES ; i++)
  {
    if (i < count)
    {
      str = MakeBaseName (GetField (OlderFiles->Strings [i], 0)) ;
      OlderMenuItems [i]->Caption = str ;
      OlderMenuItems [i]->Visible = true ;
      OlderMenuItems [i]->Hint = GetField (OlderFiles->Strings [i], 0) ;
    }
    else
      OlderMenuItems [i]->Visible = false ;
  }
  ParentForm->miOlderFiles->Enabled = count != 0 ;
}

//---------------------------------------------------------------------------
void TParentForm::SetMenuShortcuts (void)
{
  SetMenuShortcut (miUndo, pmiUndo, CMD_UNDO) ;
  SetMenuShortcut (miRedo, pmiRedo, CMD_REDO) ;
  SetMenuShortcut (miCut, pmiCut, CMD_CUT) ;
  SetMenuShortcut (miCopy, pmiCopy, CMD_COPY) ;
  SetMenuShortcut (miPaste, pmiPaste, CMD_PASTE) ;
  SetMenuShortcut (miDelete, CMD_DELETE) ;
  SetMenuShortcut (miSelectAll, CMD_SELECTALL) ;
  SetMenuShortcut (miFind, pmiFind, CMD_FIND) ;
  SetMenuShortcut (miReplace, pmiReplace, CMD_FINDREPLACE) ;
  SetMenuShortcut (miFindNext, pmiFindNext, CMD_FINDNEXT) ;
  SetMenuShortcut (miMatchBrace, pmiMatchBrace, CMD_GOTOMATCHBRACE) ;
  SetMenuShortcut (miGoToLine, pmiGoToLine, CMD_GOTOLINE) ;
  SetMenuShortcut (miProperties, pmiProperties, CMD_PROPERTIES) ;

  SetMenuShortcut (miIndentSelection, pmiIndentSelection, CMD_INDENTSELECTION) ;
  SetMenuShortcut (miExtraIndentSelection, CMD_INDENTSELECTION) ;
  SetMenuShortcut (miIndentSelectionPrevious, pmiIndentSelectionPrevious, CMD_INDENTTOPREV) ;
  SetMenuShortcut (miExtraIndentSelectionPrevious, CMD_INDENTTOPREV) ;
  SetMenuShortcut (miUndentSelection, pmiUndentSelection, CMD_UNINDENTSELECTION) ;
  SetMenuShortcut (miExtraUndentSelection, CMD_UNINDENTSELECTION) ;
  SetMenuShortcut (miUppercaseSelection, pmiUppercaseSelection, CMD_UPPERCASESELECTION) ;
  SetMenuShortcut (miLowercaseSelection, pmiLowercaseSelection, CMD_LOWERCASESELECTION) ;
  SetMenuShortcut (miSpacesToTabs, pmiSpacesToTabs, CMD_TABIFYSELECTION) ;
  SetMenuShortcut (miTabsToSpaces, pmiTabsToSpaces, CMD_UNTABIFYSELECTION) ;

  SetMenuShortcut (miShowWhiteSpace, CMD_TOGGLEWHITESPACEDISPLAY) ;
  SetMenuShortcut (miSetRepeatCount, CMD_SETREPEATCOUNT) ;
  SetMenuShortcut (miRecordMacro, CMD_RECORDMACRO) ;
  SetMenuShortcut (miPlayMacro1, CMD_PLAYMACRO1) ;
  SetMenuShortcut (miPlayMacro2, CMD_PLAYMACRO2) ;
  SetMenuShortcut (miPlayMacro3, CMD_PLAYMACRO3) ;
  SetMenuShortcut (miPlayMacro4, CMD_PLAYMACRO4) ;
  SetMenuShortcut (miPlayMacro5, CMD_PLAYMACRO5) ;
  SetMenuShortcut (miPlayMacro6, CMD_PLAYMACRO6) ;
  SetMenuShortcut (miPlayMacro7, CMD_PLAYMACRO7) ;
  SetMenuShortcut (miPlayMacro8, CMD_PLAYMACRO8) ;
  SetMenuShortcut (miPlayMacro9, CMD_PLAYMACRO9) ;
  SetMenuShortcut (miPlayMacro10, CMD_PLAYMACRO10) ;
  SetMenuShortcut (miClearAllBookmarks, pmiClearAllBookmarks, CMD_BOOKMARKCLEARALL) ;
  SetMenuShortcut (miFirstBookmark, pmiFirstBookmark, CMD_BOOKMARKJUMPTOFIRST) ;
  SetMenuShortcut (miLastBookmark, pmiLastBookmark, CMD_BOOKMARKJUMPTOLAST) ;
  SetMenuShortcut (miNextBookmark, pmiNextBookmark, CMD_BOOKMARKNEXT) ;
  SetMenuShortcut (miPreviousBookmark, pmiPreviousBookmark, CMD_BOOKMARKPREV) ;
  SetMenuShortcut (miToggleBookmark, pmiToggleBookmark, CMD_BOOKMARKTOGGLE) ;
}

//---------------------------------------------------------------------------
void AddToRecent (AnsiString FileName)
{
  AnsiString            str = GetField (FileName, 0) ;

  if (FileName == "")
    return ;
  for (int i = 0 ; i < RecentFiles->Count ; i++)
    if (GetField (RecentFiles->Strings [i], 0).AnsiCompareIC (str) == 0)
      RecentFiles->Delete (i) ;
  for (int i = 0 ; i < OlderFiles->Count ; i++)
    if (GetField (OlderFiles->Strings [i], 0).AnsiCompareIC (str) == 0)
      OlderFiles->Delete (i) ;
  RecentFiles->Insert (0, FileName) ;
  if (RecentFiles->Count == 10)
  {
    OlderFiles->Insert (0, RecentFiles->Strings [9]) ;
    if (OlderFiles->Count == MAX_OLDER_FILES)
      OlderFiles->Delete (MAX_OLDER_FILES - 1) ;
    MakeOlderMenus () ;
    RecentFiles->Delete (9) ;
  }
  MakeRecentMenus () ;
  MakeOlderMenus () ;
}

//---------------------------------------------------------------------------
void UpdateRecent (void)
{
  TCodeMax              *e ;
  AnsiString            str ;
  EditTagStruct         *t ;

  for (int i = 0 ; i < RecentFiles->Count ; i++)
  {
    str = GetField (RecentFiles->Strings [i], 0) ;
    if ((e = FindEditor (str)) == NULL)
      continue ;
    t = (EditTagStruct *) e->Tag ;
    str = t->LongName + "," ;
    str += AnsiString (e->Line) + "," ;
    str += AnsiString (e->Col) + "," ;
    str += AnsiString (e->TopLine) + "," ;
    str += AnsiString ((int) e->Language) + "," ;
    str += AnsiString (e->TabSize) + "," ;
    str += AnsiString ((int) e->AutoIndent) ;
    RecentFiles->Strings [i] = str ;
  }
  for (int i = 0 ; i < OlderFiles->Count ; i++)
  {
    str = GetField (OlderFiles->Strings [i], 0) ;
    if ((e = FindEditor (str)) == NULL)
      continue ;
    t = (EditTagStruct *) e->Tag ;
    str = t->LongName + "," ;
    str += AnsiString (e->Line) + "," ;
    str += AnsiString (e->Col) + "," ;
    str += AnsiString (e->TopLine) + "," ;
    str += AnsiString ((int) e->Language) + "," ;
    str += AnsiString (e->TabSize) + "," ;
    str += AnsiString ((int) e->AutoIndent) ;
    OlderFiles->Strings [i] = str ;
  }
}

//---------------------------------------------------------------------------
void SetLanguageBasedOnFileType (TCodeMax *e, AnsiString Name)
{
  AnsiString            ext = GetFileExt (Name).UpperCase () ;

  if (ext == "C" || ext == "CPP" || ext == "H" || ext == "HPP"  || ext == "JS")
    e->Language = cmlCCpp ;
  else if (ext == "JAVA")
    e->Language = cmlJava ;
  else if (ext == "BAS")
    e->Language = cmlBasic ;
  else if (ext == "PAS")
    e->Language = cmlPascal ;
  else if (ext == "SQL" || ext == "DDL")
    e->Language = cmlSQL ;
  else if (ext == "HTML" || ext == "HTM" || ext == "PHP" || ext == "PHP3" || ext == "PHP4"  || ext == "ASP")
    e->Language = cmlHTML ;
  else if (ext == "SGML" || ext == "XML")
    e->Language = cmlSGML ;
  else if (ext == "POV" || ext == "INC")
    e->Language = cmlPOVRay ;
}

//---------------------------------------------------------------------------
void SetStatusLine (void)
{
  CM_RANGE              range ;
  AnsiString            str ;

  if (Editor != NULL)
  {
    LastOverwrite = Editor->OvertypeMode ;
    SendMessage (StatusWindow, SB_SETTEXT, StatusIns, (LPARAM) (LastOverwrite ? "\tOvr" : "\tIns")) ;
    SendMessage (StatusWindow, SB_SETTEXT, StatusModified, (LPARAM) (Editor->Modified ? "\tMod" : "")) ;
    Editor->GetSel (&range, false) ;
    str = AnsiString ("\tL:") + (LastLine = ++range.posEnd.nLine) ;
    SendMessage (StatusWindow, SB_SETTEXT, StatusLine, (LPARAM) str.c_str ()) ;
    str = AnsiString ("\tC:") + (LastCol = ++range.posEnd.nCol) ;
    SendMessage (StatusWindow, SB_SETTEXT, StatusCol, (LPARAM) str.c_str ()) ;
  }
  else
  {
    SendMessage (StatusWindow, SB_SETTEXT, StatusIns, (LPARAM) "") ;
    SendMessage (StatusWindow, SB_SETTEXT, StatusModified, (LPARAM) "") ;
    SendMessage (StatusWindow, SB_SETTEXT, StatusLine, (LPARAM) "") ;
    SendMessage (StatusWindow, SB_SETTEXT, StatusCol, (LPARAM) "") ;
  }
}

void MakeLogicalFont (HDC hdc, int size, char *facename, LOGFONT *lf)
{
  memset (lf, 0, sizeof (LOGFONT)) ;
  lf->lfHeight = -MulDiv (size, GetDeviceCaps (hdc, LOGPIXELSY), 72) ;
  lf->lfWeight = FW_NORMAL ;
  lf->lfPitchAndFamily = FIXED_PITCH | FF_MODERN ;
  lf->lfCharSet = DEFAULT_CHARSET ;
  lf->lfQuality = PROOF_QUALITY ;
  strncpy (lf->lfFaceName, facename, sizeof (lf->lfFaceName) - 1) ;
}

//---------------------------------------------------------------------------
void TParentForm::GetSettings (TRegDef *t, bool RestoreFiles)
{
  LOGFONT               lf ;
  AnsiString            str ;
  AnsiString            params ;
  TCodeMax              *e ;

  ec.AutoIndent = (TAutoIndent) t->ReadInt ("AutoIndent", CM_INDENT_PREVLINE) ;
  ec.SyntaxHighlighting = t->ReadBool ("SyntaxHighlighting", true) ;
  ec.WhiteSpaceDisplay = t->ReadBool ("WhiteSpaceDisplay", false) ;
  ec.TabExpand = t->ReadBool ("TabExpand", true) ;
  ec.SmoothScrolling = t->ReadBool ("SmoothScrolling", false) ;
  ec.LineToolTips = t->ReadBool ("LineToolTips", true) ;
  ec.LeftMarginVisible = t->ReadBool ("LeftMarginVisible", true) ;
  ec.CaseSensitive = t->ReadBool ("CaseSensitive", false) ;
  ec.PreserveCase = t->ReadBool ("PreserveCase", true) ;
  ec.WholeWordEnabled = t->ReadBool ("WholeWordEnabled", false) ;
  ec.DragDropEnabled = t->ReadBool ("DragDropEnabled", true) ;
  ec.HSplitterEnabled = t->ReadBool ("HSplitterEnabled", true) ;
  ec.VSplitterEnabled = t->ReadBool ("VSplitterEnabled", true) ;
  ec.ColumnSelEnabled = t->ReadBool ("ColumnSelEnabled", true) ;
  ec.RegexpEnabled = t->ReadBool ("RegexpEnabled", false) ;
  ec.OvertypeCaret = t->ReadBool ("OvertypeCaret", true) ;
  ec.SelBoundsEnabled = t->ReadBool ("SelBoundsEnabled", false) ;
  ec.TabKeywordExpansion = t->ReadBool ("TabKeywordExpansion", true) ;
  ec.ScrollBars = (TScrollStyle) t->ReadInt ("ScrollBars", ssBoth) ;
  ec.TabSize = t->ReadInt ("TabSize", 8) ;
  ec.UndoLimit = t->ReadInt ("UndoLimit", -1) ;
  ec.DefaultFlags = t->ReadInt ("DefaultFlags", 0) ;
  t->ReadBin ("Colours", (char *) &ec.Colours, sizeof (CM_COLORS), (char *) &DefaultColours) ;
  t->ReadBin ("FontStyles", (char *) &ec.FontStyles, sizeof (CM_FONTSTYLES), (char *) &DefaultFontStyles) ;
  ec.HotKeyLen = t->ReadBin ("KeyBindings", &ec.HotKeys, 0, NULL) ;

  // sanity-check the key bindings
  if (ec.HotKeyLen > 0 && ec.HotKeyLen < 512)
  {
    ec.HotKeyLen = 0 ;
    delete [] ec.HotKeys ;
    ec.HotKeys = NULL ;
    ShowMessage ("Warning: Did not restore editor key bindings (possible corruption.)") ;
  }

  ec.FindMRUList = t->ReadString ("FindMRUList", "") ;
  if (ec.FindMRUList == "<empty>")
    ec.FindMRUList = "" ;
  ec.ReplaceMRUList = t->ReadString ("ReplaceMRUList", "") ;
  if (ec.ReplaceMRUList == "<empty>")
    ec.ReplaceMRUList = "" ;
  ec.HFont = NULL ;
  if (t->ReadBin ("Font", (char *) &lf, 0, NULL) == sizeof (lf))
  {
    lf.lfQuality = PROOF_QUALITY ;
    ec.HFont = CreateFontIndirect (&lf) ;
  }
  if (ec.HFont == NULL)
  {
    HDC hDC = GetDC (NULL) ;
    MakeLogicalFont (hDC, 8, "Lucida Console", &lf) ;
    ec.HFont = CreateFontIndirect (&lf) ;
    if (ec.HFont == NULL)
    {
      MakeLogicalFont (hDC, 8, "Courier New", &lf) ;
      ec.HFont = CreateFontIndirect (&lf) ;
    }
    if (ec.HFont == NULL)
    {
      MakeLogicalFont (hDC, 11, "Fixedsys", &lf) ;
      ec.HFont = CreateFontIndirect (&lf) ;
    }
    if (ec.HFont == NULL)
      ShowMessage ("Warning: could not create editor font. Set font manually from editor properties menu.") ;
    ReleaseDC (NULL, hDC) ;
  }

  UndoAfterSave = t->ReadBool ("UndoAfterSave", true) ;
  AutoReload = t->ReadInt ("AutoReload", 1) ;
  CreateBackups = t->ReadBool ("CreateBackups", true) ;
  EditStartDragOffset = EditDragOffset = t->ReadInt ("SmallMessageWindowSize", 96) ;

  TRegDef *macroReg = new TRegDef (t->CurrentPath + "\\Macro") ;
  for (int i = 0 ; i < CM_MAX_MACROS ; i++)
    ec.MacroLen [i] = macroReg->ReadBin (AnsiString ("Macro") + i, ec.Macros + i, 0, NULL) ;
  delete macroReg ;

  TRegDef *olderReg = new TRegDef (t->CurrentPath + "\\Older") ;
  for (int i = MAX_OLDER_FILES - 1 ; i >= 0 ; i--)
  {
    str = AnsiString ("Older") + i ;
    str = olderReg->ReadString (str, "") ;
    if (str != "")
      OlderFiles->Insert (0, str) ;
  }
  delete olderReg ;

  TRegDef *recentReg = new TRegDef (t->CurrentPath + "\\Recent") ;
  for (int i = 8 ; i >= 0 ; i--)
  {
    str = AnsiString ("Recent") + i ;
    str = recentReg->ReadString (str, "") ;
    if (str != "")
      RecentFiles->Insert (0, str) ;
  }
  delete recentReg ;

  if (RestoreFiles)
  {
    TRegDef *openReg = new TRegDef (t->CurrentPath + "\\Open") ;
    for (int i = 0 ; i < MAX_EDITORS ; i++)
    {
      str = AnsiString ("Open") + i ;
      params = openReg->ReadString (str, "") ;
      if (params == "" || params [1] == ',')
        continue ;
      if ((e = CreateNewEditor (GetNextField (params).c_str (), false, false)) == NULL)
        continue ;
      try
      {
        e->Line = GetNextField (params).ToInt () ;
        e->Col = GetNextField (params).ToInt () ;
        e->TopLine = GetNextField (params).ToInt () ;
        // From TCodeMax.h
        // enum TLanguage {cmlNone, cmlCCpp, cmlBasic, cmlJava, cmlPascal, cmlSQL, cmlPOVRay, cmlHTML, cmlSGML} ;
        e->Language = (TLanguage) GetNextField (params).ToInt () ;
        e->TabSize = GetNextField (params).ToInt () ;
        e->AutoIndent = (TAutoIndent) GetNextField (params).ToInt () ;
      }
      catch (EConvertError& E)
      {
        continue ;
      }
    }
    delete openReg ;
  }

  UpdateRecent () ;

  InitialDir = t->ReadString ("InitialDir", HomePath + "Scenes") ;
  Tab->TabIndex = t->ReadInt ("CurrentTab", 0) ;
  if (Tab->TabIndex < 0)
    Tab->TabIndex = 0 ;
  miShowParseMessages->Checked = t->ReadBool ("ShowParseMessages", true) ;
  miAutoLoadErrorFile->Checked = t->ReadBool ("AutoLoadErrorFile", true) ;
  AutoSaveTimer->Interval = t->ReadInt ("AutoSaveTime", 0) ;
  AutoSaveTimer->Enabled = AutoSaveTimer->Interval > 0 ;
  miCursorBeyondEOL->Checked = t->ReadBool ("CursorBeyondEOL", true) ;
  miConstrainCaret->Checked = ec.SelBoundsEnabled ;
  if (miConstrainCaret->Checked)
  {
    miCursorBeyondEOL->Checked = false ;
    miCursorBeyondEOL->Enabled = false ;
  }
  miOverlayKeywordExpansion->Checked = ec.TabKeywordExpansion ;

  try
  {
    PrintForm->PageBreaks->Checked = t->ReadBool ("PrintPageBreaks", false) ;
    PrintForm->LineNumbers->Checked = t->ReadBool ("PrintLineNumbers", false) ;
    PrintForm->Header->Checked = t->ReadBool ("PrintHeader", true) ;
    PrintForm->WrapLines->Checked = t->ReadBool ("WrapLongLines", false) ;
    PrintForm->HighlightComments->Checked = t->ReadBool ("HighlightComments", true) ;
    PrintForm->TwoUpPrinting->Checked = t->ReadBool ("TwoUpPrinting", false) ;
    PrintForm->LeftMargin->Value = t->ReadInt ("LeftMargin", 5) ;
    PrintForm->TopMargin->Value = t->ReadInt ("TopMargin", 5) ;
    PrintForm->RightMargin->Value = t->ReadInt ("RightMargin", 5) ;
    PrintForm->BottomMargin->Value = t->ReadInt ("BottomMargin", 5) ;
  }
  catch (...)
  {
    // we can get here if there's no printer as TwoUpPrinting (at the very least)
    // will cause an EPrinter exception.
  }

  str = t->ReadString ("PrinterFontStyle", "") ;
  PrintForm->FontDialog->Font->Style.Clear () ;
  if (str.Pos ("B"))
    PrintForm->FontDialog->Font->Style = PrintForm->FontDialog->Font->Style << fsBold ;
  if (str.Pos ("I"))
    PrintForm->FontDialog->Font->Style = PrintForm->FontDialog->Font->Style << fsItalic ;
  if (str.Pos ("U"))
    PrintForm->FontDialog->Font->Style = PrintForm->FontDialog->Font->Style << fsUnderline ;
  if (str.Pos ("S"))
    PrintForm->FontDialog->Font->Style = PrintForm->FontDialog->Font->Style << fsStrikeOut ;
  PrintForm->FontDialog->Font->Charset = (byte) t->ReadInt ("PrinterFontCharset", PrintForm->FontDialog->Font->Charset) ;
  PrintForm->FontDialog->Font->Name = t->ReadString ("PrinterFontName", "Lucida Console") ;
  PrintForm->FontDialog->Font->Size = t->ReadInt ("PrinterFontSize", 8) ;

  HDC hDC = GetDC (NULL) ;
  MakeLogicalFont (hDC, 8, PrintForm->FontDialog->Font->Name.c_str (), &lf) ;
  HFONT TempFont = CreateFontIndirect (&lf) ;
  if (TempFont == NULL)
  {
    PrintForm->FontDialog->Font->Name = "Courier New" ;
    MakeLogicalFont (hDC, 8, PrintForm->FontDialog->Font->Name.c_str (), &lf) ;
    TempFont = CreateFontIndirect (&lf) ;
  }
  if (TempFont == NULL)
  {
    PrintForm->FontDialog->Font->Name = "Fixedsys" ;
    MakeLogicalFont (hDC, 8, PrintForm->FontDialog->Font->Name.c_str (), &lf) ;
    TempFont = CreateFontIndirect (&lf) ;
  }
  if (TempFont != NULL)
    DeleteObject (TempFont) ;
  ReleaseDC (NULL, hDC) ;

  SetMenuState () ;
}

//---------------------------------------------------------------------------
void StoreEditor (TCodeMax *e)
{
  if (e != NULL)
  {
    ec.AutoIndent = e->AutoIndent ;
    ec.SyntaxHighlighting = e->SyntaxHighlighting ;
    ec.WhiteSpaceDisplay = e->WhiteSpaceDisplay ;
    ec.TabExpand = e->TabExpand ;
    ec.LeftMarginVisible = e->LeftMarginVisible ;
    ec.SmoothScrolling = e->SmoothScrolling ;
    ec.LineToolTips = e->LineToolTips ;
    ec.CaseSensitive = e->CaseSensitive ;
    ec.PreserveCase = e->PreserveCase ;
    ec.WholeWordEnabled = e->WholeWordEnabled ;
    ec.DragDropEnabled = e->DragDropEnabled ;
    ec.HSplitterEnabled = e->HSplitterEnabled ;
    ec.VSplitterEnabled = e->VSplitterEnabled ;
    ec.ColumnSelEnabled = e->ColumnSelEnabled ;
    ec.RegexpEnabled = e->RegexpEnabled ;
    ec.OvertypeCaret = e->OvertypeCaret ;
    ec.SelBoundsEnabled = e->SelBoundsEnabled ;
    ec.ScrollBars = e->ScrollBars ;
    ec.TabSize = e->TabSize ;
    ec.UndoLimit = e->UndoLimit ;
    e->GetColors (&ec.Colours) ;
    e->GetFontStyles (&ec.FontStyles) ;
    ec.HFont = e->GetFont () ;
  }
  else
  {
    TCodeMax::GetFindReplaceMRUList (ec.FindMRUList, true) ;
    TCodeMax::GetFindReplaceMRUList (ec.ReplaceMRUList, false) ;
    if (ec.HotKeys != NULL)
      delete [] ec.HotKeys ;
    ec.HotKeys = NULL ;
    if ((ec.HotKeyLen = TCodeMax::GetHotKeys (NULL)) > 0)
    {
      ec.HotKeys = new char [ec.HotKeyLen] ;
      if ((ec.HotKeyLen = TCodeMax::GetHotKeys (ec.HotKeys)) == 0)
      {
        delete [] ec.HotKeys ;
        ec.HotKeys = NULL ;
        PutStatusMessage ("Failed to get key bindings from Editor DLL") ;
      }
    }
    for (int i = 0 ; i < CM_MAX_MACROS ; i++)
    {
      delete [] ec.Macros [i] ;
      ec.Macros [i] = NULL ;
      if ((ec.MacroLen [i] = TCodeMax::GetMacro (i, NULL)) <= 0)
        continue ;
      ec.Macros [i] = new char [ec.MacroLen [i]] ;
      TCodeMax::GetMacro (i, ec.Macros [i]) ;
    }
  }
}

//---------------------------------------------------------------------------
void TParentForm::PutSettings (TRegDef *t)
{
  LOGFONT               lf ;
  TCodeMax              *e ;
  AnsiString            str ;
  AnsiString            params ;
  EditTagStruct         *tag ;

  if (Editor != NULL)
    StoreEditor (Editor) ;
  StoreEditor (NULL) ;
  t->WriteInteger ("DefaultFlags", ec.DefaultFlags) ;
  t->WriteInteger ("AutoIndent", ec.AutoIndent) ;
  t->WriteBool ("SyntaxHighlighting", ec.SyntaxHighlighting) ;
  t->WriteBool ("WhiteSpaceDisplay", ec.WhiteSpaceDisplay) ;
  t->WriteBool ("TabExpand", ec.TabExpand) ;
  t->WriteBool ("SmoothScrolling", ec.SmoothScrolling) ;
  t->WriteBool ("LineToolTips", ec.LineToolTips) ;
  t->WriteBool ("LeftMarginVisible", ec.LeftMarginVisible) ;
  t->WriteBool ("CaseSensitive", ec.CaseSensitive) ;
  t->WriteBool ("PreserveCase", ec.PreserveCase) ;
  t->WriteBool ("WholeWordEnabled", ec.WholeWordEnabled) ;
  t->WriteBool ("DragDropEnabled", ec.DragDropEnabled) ;
  t->WriteBool ("HSplitterEnabled", ec.HSplitterEnabled) ;
  t->WriteBool ("VSplitterEnabled", ec.VSplitterEnabled) ;
  t->WriteBool ("ColumnSelEnabled", ec.ColumnSelEnabled) ;
  t->WriteBool ("RegexpEnabled", ec.RegexpEnabled) ;
  t->WriteBool ("OvertypeCaret", ec.OvertypeCaret) ;
  t->WriteBool ("SelBoundsEnabled", ec.SelBoundsEnabled) ;
  t->WriteBool ("TabKeywordExpansion", ec.TabKeywordExpansion) ;
  t->WriteInteger ("ScrollBars", (int) ec.ScrollBars) ;
  t->WriteInteger ("TabSize", ec.TabSize) ;
  t->WriteInteger ("UndoLimit", ec.UndoLimit) ;
  t->WriteBinaryData ("Colours", &ec.Colours, sizeof (CM_COLORS)) ;
  t->WriteBinaryData ("FontStyles", &ec.FontStyles, sizeof (CM_FONTSTYLES)) ;
  if (ec.HotKeys && ec.HotKeyLen >= 512)
    t->WriteBinaryData ("KeyBindings", ec.HotKeys, ec.HotKeyLen) ;
  t->WriteString ("FindMRUList", ec.FindMRUList == "" ? AnsiString ("<empty>") : ec.FindMRUList) ;
  t->WriteString ("ReplaceMRUList", ec.ReplaceMRUList == "" ? AnsiString ("<empty>") : ec.ReplaceMRUList) ;
  t->WriteBool ("UndoAfterSave", UndoAfterSave) ;
  t->WriteBool ("CreateBackups", CreateBackups) ;
  t->WriteInteger ("AutoReload", AutoReload) ;
  t->WriteInteger ("SmallMessageWindowSize", EditDragOffset) ;
  if (GetObject (ec.HFont, sizeof (LOGFONT), &lf))
    t->WriteBinaryData ("Font", &lf, sizeof (LOGFONT)) ;

  TRegDef *macroReg = new TRegDef (t->CurrentPath + "\\Macro") ;
  for (int i = 0 ; i < CM_MAX_MACROS ; i++)
  {
    str = AnsiString ("Macro") + i ;
    if (ec.MacroLen [i] == 0)
    {
      macroReg->DeleteValue (str) ;
      continue ;
    }
    macroReg->WriteBinaryData (str, ec.Macros [i], ec.MacroLen [i]) ;
  }
  delete macroReg ;

  UpdateRecent () ;

  TRegDef *recentReg = new TRegDef (t->CurrentPath + "\\Recent") ;
  for (int i = 0 ; i < 9 ; i++)
  {
    str = AnsiString ("Recent") + i ;
    if (i >= RecentFiles->Count)
    {
      recentReg->DeleteValue (str) ;
      continue ;
    }
    recentReg->WriteString (str, RecentFiles->Strings [i]) ;
  }
  delete recentReg ;

  TRegDef *olderReg = new TRegDef (t->CurrentPath + "\\Older") ;
  for (int i = 0 ; i < MAX_OLDER_FILES ; i++)
  {
    str = AnsiString ("Older") + i ;
    if (i >= OlderFiles->Count)
    {
      olderReg->DeleteValue (str) ;
      continue ;
    }
    olderReg->WriteString (str, OlderFiles->Strings [i]) ;
  }
  delete olderReg ;

  TRegDef *openReg = new TRegDef (t->CurrentPath + "\\Open") ;
  for (int i = 0 ; i < MAX_EDITORS ; i++)
  {
    str = AnsiString ("Open") + i ;
    if (i >= EditorCount)
    {
      openReg->DeleteValue (str) ;
      continue ;
    }
    e = Editors [i] ;
    tag = (EditTagStruct *) e->Tag ;
    if (tag->LongName == "")
    {
      openReg->DeleteValue (str) ;
      continue ;
    }
    params = tag->LongName + "," ;
    params += AnsiString (e->Line) + "," ;
    params += AnsiString (e->Col) + "," ;
    params += AnsiString (e->TopLine) + "," ;
    params += AnsiString ((int) e->Language) + "," ;
    params += AnsiString (e->TabSize) + "," ;
    params += AnsiString ((int) e->AutoIndent) ;
    openReg->WriteString (str, params) ;
  }
  delete openReg ;

  t->WriteString ("InitialDir", InitialDir) ;
  t->WriteInteger ("CurrentTab", Tab->TabIndex) ;  
  t->WriteBool ("ShowParseMessages", miShowParseMessages->Checked) ;
  t->WriteBool ("AutoLoadErrorFile", miAutoLoadErrorFile->Checked) ;
  t->WriteInteger ("AutoSaveTime", AutoSaveTimer->Interval) ;
  t->WriteBool ("CursorBeyondEOL", miCursorBeyondEOL->Checked) ;

  t->WriteBool ("PrintPageBreaks", PrintForm->PageBreaks->Checked) ;
  t->WriteBool ("PrintLineNumbers", PrintForm->LineNumbers->Checked) ;
  t->WriteBool ("PrintHeader", PrintForm->Header->Checked) ;
  t->WriteBool ("WrapLongLines", PrintForm->WrapLines->Checked) ;
  t->WriteBool ("HighlightComments", PrintForm->HighlightComments->Checked) ;
  t->WriteBool ("TwoUpPrinting", PrintForm->TwoUpPrinting->Checked) ;
  t->WriteInteger ("LeftMargin", PrintForm->LeftMargin->Value) ;
  t->WriteInteger ("TopMargin", PrintForm->TopMargin->Value) ;
  t->WriteInteger ("RightMargin", PrintForm->RightMargin->Value) ;
  t->WriteInteger ("BottomMargin", PrintForm->BottomMargin->Value) ;
  t->WriteString ("PrinterFontName", PrintForm->FontDialog->Font->Name) ;
  t->WriteInteger ("PrinterFontSize", PrintForm->FontDialog->Font->Size) ;

  str = "" ;
  if (PrintForm->FontDialog->Font->Style.Contains (fsBold))
    str += "B" ;
  if (PrintForm->FontDialog->Font->Style.Contains (fsItalic))
    str += "I" ;
  if (PrintForm->FontDialog->Font->Style.Contains (fsUnderline))
    str += "U" ;
  if (PrintForm->FontDialog->Font->Style.Contains (fsStrikeOut))
    str += "S" ;
  t->WriteString ("PrinterFontStyle", str) ;
  t->WriteInteger ("PrinterFontCharset", PrintForm->FontDialog->Font->Charset) ;
}

//---------------------------------------------------------------------------
void SetupEditor (TCodeMax *e, bool PropsChange)
{
//CM_HOTKEY   hotkey ;

  if (e != NULL)
  {
    if (!PropsChange)
    {
      e->AutoIndent = ec.AutoIndent ;
      e->TabSize = ec.TabSize ;
    }
    if (ec.HFont != NULL)
      e->SetFont (ec.HFont) ;
    e->SetFontOwnership (false) ;
    e->TabExpand = ec.TabExpand ;
    e->SyntaxHighlighting = ec.SyntaxHighlighting ;
    e->WhiteSpaceDisplay = ec.WhiteSpaceDisplay ;
    e->SmoothScrolling = ec.SmoothScrolling ;
    e->LineToolTips = ec.LineToolTips ;
    e->LeftMarginVisible = ec.LeftMarginVisible ;
    e->CaseSensitive = ec.CaseSensitive ;
    e->PreserveCase = ec.PreserveCase ;
    e->WholeWordEnabled = ec.WholeWordEnabled ;
    e->DragDropEnabled = ec.DragDropEnabled ;
    e->ScrollBars = ec.ScrollBars ;
    e->HSplitterEnabled = ec.HSplitterEnabled ;
    e->VSplitterEnabled = ec.VSplitterEnabled ;
    e->ColumnSelEnabled = ec.ColumnSelEnabled ;
    e->UndoLimit = ec.UndoLimit ;
    e->SetColors (&ec.Colours) ;
    e->SetFontStyles (&ec.FontStyles) ;
    e->RegexpEnabled = ec.RegexpEnabled ;
    e->OvertypeCaret = ec.OvertypeCaret ;
    e->SelBoundsEnabled = ec.SelBoundsEnabled ;
  }
  if (EditorCount == 1)
  {
    TCodeMax::RegisterCommand (CMD_NEXT_KEYWORD, "ExpandNextKeyword", "Expands the next keyword in the keyword list") ;
    TCodeMax::RegisterCommand (CMD_PREV_KEYWORD, "ExpandPrevKeyword", "Expands the previous keyword in the keyword list") ;
    try
    {
      if (ec.HotKeyLen > 0 && ec.HotKeys != NULL)
        TCodeMax::SetHotKeys (ec.HotKeys) ;
    }
    catch (...)
    {
      ShowMessage ("Failed to restore editor key bindings") ;
    }
    if ((ec.DefaultFlags & 0x01) == 0)
    {
//    memset (&hotkey, 0, sizeof (hotkey)) ;
//    hotkey.byModifiers1 = HOTKEYF_CONTROL ;
//    hotkey.nVirtKey1 = ' ' ;
//    TCodeMax::RegisterHotKey (&hotkey, CMD_CODELIST) ;
//    hotkey.nVirtKey1 = 'A' ;
//    TCodeMax::RegisterHotKey (&hotkey, CMD_SELECTALL) ;
      ec.DefaultFlags |= 0x01 ;
    }
    TCodeMax::SetFindReplaceMRUList (ec.FindMRUList, true) ;
    TCodeMax::SetFindReplaceMRUList (ec.ReplaceMRUList, false) ;
    for (int i = 0 ; i < CM_MAX_MACROS ; i++)
      if (ec.Macros [i] != NULL)
        TCodeMax::SetMacro (i, ec.Macros [i]) ;
  }
}

//---------------------------------------------------------------------------
void ClearBMPHint (void)
{
  if (CurrentHintWindow != NULL)
  {
    delete CurrentHintWindow ;
    CurrentHintWindow = NULL ;
  }
}

//---------------------------------------------------------------------------
void ClearBMPHint (UINT Msg, UINT WParam, UINT LParam)
{
  if (CurrentHintWindow != NULL)
  {
    try
    {
      if (!CurrentHintWindow->IsHintMessage (Msg))
      {
        delete CurrentHintWindow ;
        CurrentHintWindow = NULL ;
      }
    }
    catch (...)
    {
    }
  }
}

//---------------------------------------------------------------------------
void ShowBMPHint (TMenuItem *m)
{
  RECT rect ;
  POINT point ;
  Graphics::TBitmap *bmp = new Graphics::TBitmap () ;
  try
  {
    AnsiString filename = m->Hint ;
    filename.Delete (1, 9) ;
    bmp->LoadFromFile (filename) ;

    // this only seems to work on W2K or later
    if (GetMenuItemRect (NULL, m->Parent->Handle, m->MenuIndex, &rect))
    {
      rect.right = rect.left - 4 ;
      rect.left = rect.right - bmp->Width - 4 ;
      rect.top -= (bmp->Height + 4 + rect.top - rect.bottom - 1) / 2 ;
      rect.bottom = rect.top + bmp->Height + 4 ;
    }
    else
    {
      // we -cannot- rely on GetMenuItemRect to return useful co-ordinates
      // if given a window handle (no matter what the Microsoft docs say).
      bool ok = false ;
      GetCursorPos (&point) ;
      HWND hwnd = WindowFromPoint (point) ;
      if (hwnd != NULL)
      {
        char str [32] ;
        if (GetClassName (hwnd, str, sizeof (str)) && strcmp (str, "#32768") == 0)
        {
          RECT menuRect ;
          GetWindowRect (hwnd, &menuRect) ;
          int border = GetSystemMetrics (SM_CXEDGE) ;
          if (GetMenuItemRect (MainWindow, m->Parent->Handle, 0, &rect))
          {
            int x = rect.left ;
            int y = rect.top ;
            if (GetMenuItemRect (MainWindow, m->Parent->Handle, m->MenuIndex, &rect))
            {
              x = rect.left - x ;
              OffsetRect (&rect, -rect.left, -y) ;
              OffsetRect (&rect, menuRect.left + x, menuRect.top) ;
              OffsetRect (&rect, border, border) ;
              rect.right = rect.left - 4 ;
              rect.left = rect.right - bmp->Width - 4 ;
              rect.top -= (bmp->Height + 4 + rect.top - rect.bottom - 1) / 2 ;
              rect.bottom = rect.top + bmp->Height + 4 ;
              ok = true ;
            }
          }
        }
      }
      if (!ok)
      {
        // do the best we can here ... assume that the cursor is where the
        // user wants the image to be shown (not necessarily the case if
        // they are navigating with the keyboard, but what can we do ? ...)
        rect.left = point.x - bmp->Width / 2 ;
        rect.right = rect.left + bmp->Width + 4 ;
        rect.bottom = point.y - 5 ;
        rect.top = rect.bottom - bmp->Height - 4 ;
      }
    }
  }
  catch (...)
  {
    delete bmp ;
    return ;
  }
  CurrentHintWindow = new TImageHintWindow (MainWindow, rect, bmp) ;
}

//---------------------------------------------------------------------------
AnsiString GetCurrentKeyword (TCodeMax *Editor)
{
  AnsiString ln = Editor->GetLine (Editor->Line) ;
  const char *line = ln.c_str () ;
  int col = Editor->Col - 1 ;
  const char *s = line + col ;

  // special case - if the cursor is immediately after the end of a word,
  // and it's on whitespace or a non-alpha, then we go back one column
  if (col > 0 && !isalpha (*s) && *s != '_')
    if (isalnum (line [col - 1]) || line [col - 1] == '_' || line [col - 1] == '#')
      s-- ;

  // backtrack to the start of the word
  while ((isalnum (*s) || *s == '_' || *s == '#') && s > line)
    s-- ;

  // now we need to track forward
  while (*s && !isalnum (*s) && *s != '_' && *s != '#')
    s++ ;

  // return if the line is blank
  if (*s == '\0')
    return ("") ;

  // now find the end of the word
  const char *start = s ;
  while (*s && (isalnum (*s) || *s == '_' || *s == '#'))
    s++ ;
  return (ln.SubString (start - line + 1, s - start)) ;
}

//---------------------------------------------------------------------------
void SelectClosestItem (HWND hWnd, AnsiString Item)
{
  int         index ;
  int         itemheight ;
  int         lvheight ;
  int         visitems ;
  int         desiredtop ;
  int         delta ;
  int         currenttop ;
  RECT        rect ;
  LVFINDINFO  lvfi ;

  if (Item != NULL)
  {
    lvfi.psz = Item.c_str () ;
    lvfi.flags = LVFI_STRING | LVFI_PARTIAL ;
    index = ListView_FindItem (hWnd, -1, &lvfi) ;
  }
  else
    index = ListView_GetNextItem (hWnd, -1, LVIS_SELECTED) ;

  if (index != -1)
  {
    ListView_SetItemState (hWnd, index, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED) ;
    ListView_EnsureVisible (hWnd, index, FALSE) ;
    ListView_GetItemRect (hWnd, index, &rect, LVIR_BOUNDS) ;
    itemheight = rect.bottom - rect.top ;
    GetClientRect (hWnd, &rect) ;
    lvheight = rect.bottom ;
    visitems = lvheight / itemheight ;
    desiredtop = index - visitems / 2 ;
    if (desiredtop >= 0)
    {
      currenttop = ListView_GetTopIndex (hWnd) ;
      delta = (desiredtop - currenttop) * itemheight ;
      ListView_Scroll (hWnd, 0, delta) ;
      ListView_EnsureVisible (hWnd, index, FALSE) ;
    }
  }
}

//---------------------------------------------------------------------------
void ActionSelection (TCodeMax *Editor, LPCSTR Text)
{
  AnsiString ln = Editor->GetLine (Editor->Line) ;
  const char *line = ln.c_str () ;
  int col = Editor->Col - 1 ;
  const char *s = line + col ;
  CM_RANGE range ;

  range.posStart.nLine = range.posEnd.nLine = Editor->Line - 1 ;
  if (isalnum (*s) || *s == '_')
  {
    // we seem to be within a word
    while (s-- > line)
      if (!(isalnum (*s) || *s == '_'))
        break ;
    range.posStart.nCol = (int) (++s - line) ;
    while (*++s)
      if (!(isalnum (*s) || *s == '_'))
        break ;
    range.posEnd.nCol = (int) (s - line) ;
  }
  else
  {
    // it's either a space or the end of the line.
    range.posEnd.nCol = col ;
    while (s-- > line)
      if (!(isalnum (*s) || *s == '_'))
        break ;
    range.posStart.nCol = (int) (++s - line) ;
  }

  Editor->ReplaceText (Text, &range) ;
  Editor->SetCaretPos (range.posStart.nCol + strlen (Text) + 1, range.posStart.nLine + 1) ;
}

//---------------------------------------------------------------------------
int KeywordMatches (LPCSTR Text)
{
  int         matches = 0 ;
  int         len = strlen (Text) ;

  for (int i = 0 ; i < KeywordCount ; i++)
    if (strncmp (Keywords [i], Text, len) == 0)
      matches++ ;
  return (matches) ;
}

//---------------------------------------------------------------------------
bool CompleteMatch (LPCSTR Text)
{
  for (int i = 0 ; i < KeywordCount ; i++)
    if (strcmp (Keywords [i], Text) == 0)
      return (true) ;
  return (false) ;
}

//---------------------------------------------------------------------------
bool SingleKeywordMatch (TCodeMax *Editor, LPCSTR Text)
{
  int         matches = 0 ;
  int         index ;
  int         len = strlen (Text) ;

  for (int i = 0 ; i < KeywordCount ; i++)
  {
    if (strncmp (Keywords [i], Text, len) == 0)
    {
      matches++ ;
      index = i ;
    }
  }
  if (matches != 1)
    return (false) ;
  ActionSelection (Editor, Keywords [index]) ;
  return (true) ;
}

//---------------------------------------------------------------------------
bool TParentForm::KeywordExpansion (TCodeMax *Editor, bool Reverse, bool OverlayCommand)
{
  int         col = Editor->Col - 1 ;
  char        *keyword = NULL ;
  CM_RANGE    range ;

  if (OverlayCommand)
  {
    Editor->GetSel (&range) ;
    if (memcmp (&range.posStart, &range.posEnd, sizeof (range.posStart)) != 0)
      return (false) ;
  }

  AnsiString ln = Editor->GetLine (Editor->Line) ;
  const char *line = ln.c_str () ;

  if (ExpansionAnchorCol == -1)
  {
    const char *s = line + col ;
    if (isalnum (*s) || *s == '_')
    {
      // we are potentially in the middle of an existing keyword or identifier - bail out.
      if (!OverlayCommand)
      {
        FlashWindow (MainWindow, TRUE) ;
        FlashWindow (MainWindow, FALSE) ;
      }
      return (false) ;
    }
    while (s-- > line)
      if (!(isalnum (*s) || *s == '_'))
        break ;
    ExpansionWord = ++s ;
    if (OverlayCommand)
      if (s == line + col)
        return (false) ;
    ExpansionAnchorCol = (int) (s - line) ;
    ExpansionAnchorLine = Editor->Line - 1 ;
    ExpansionLastCol = col ;
    ExpansionIndex = -1 ;
    ExpansionWord.SetLength (col - ExpansionAnchorCol) ;
    ExpansionHasCompleteMatch = CompleteMatch (ExpansionWord.c_str ()) ;
    if (ExpansionHasCompleteMatch)
    {
      if (OverlayCommand)
      {
        ExpansionAnchorCol = -1 ;
        return (false) ;
      }
      ExpansionIndex = 0 ;
    }
  }

  range.posStart.nLine = range.posEnd.nLine = ExpansionAnchorLine ;
  range.posStart.nCol = ExpansionAnchorCol ;
  range.posEnd.nCol = col ;

  int len = ExpansionWord.Length () ;
  char *text = ExpansionWord.c_str () ;
  int total = KeywordMatches (text) ;
  if (total == 0)
  {
    if (!OverlayCommand)
    {
      FlashWindow (MainWindow, TRUE) ;
      FlashWindow (MainWindow, FALSE) ;
    }
    return (false) ;
  }
  int matches = 0 ;
  int minindex = ExpansionHasCompleteMatch ? 0 : -1 ;

  if (Reverse)
  {
    if (ExpansionIndex < minindex + 1)
    {
      FlashWindow (MainWindow, TRUE) ;
      FlashWindow (MainWindow, FALSE) ;
      return (true) ;
    }
    if (--ExpansionIndex == minindex)
      keyword = text ;
  }
  else
  {
    if (ExpansionIndex >= total - 1)
    {
      FlashWindow (MainWindow, TRUE) ;
      FlashWindow (MainWindow, FALSE) ;
      return (true) ;
    }
    ExpansionIndex++ ;
  }

  for (int i = 0 ; keyword == NULL && i < KeywordCount ; i++)
    if (strncmp (Keywords [i], text, len) == 0)
      if (matches++ == ExpansionIndex)
        keyword = Keywords [i] ;

  if (keyword != NULL)
  {
    ExpansionLastCol = range.posStart.nCol + strlen (keyword) ;
    Editor->SetCaretPos (ExpansionLastCol + 1, ExpansionAnchorLine + 1) ;
    Editor->ReplaceText (keyword, &range) ;
  }
  else
  {
    FlashWindow (MainWindow, TRUE) ;
    FlashWindow (MainWindow, FALSE) ;
  }

  return (true) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::EditParentWndProc (Messages::TMessage& Message)
{
  int                   len ;
  char                  str [256] ;
  NMHDR                 *nmh ;
  POINT                 point ;
  LVITEM                item ;
  CM_RANGE              range ;
  CM_KEYDATA            *keydata ;
  CM_CODELISTDATA       *codelist ;
  CM_REGISTEREDCMDDATA  *cmddata ;

  ClearBMPHint (Message.Msg, Message.WParam, Message.LParam) ;
  if (Editor != NULL)
  {
    if (Message.Msg == WM_NOTIFY)
    {
      nmh = (NMHDR *) Message.LParam ;
      if (nmh->hwndFrom == Editor->Handle)
      {
        switch (nmh->code)
        {
          case CMN_REGISTEREDCMD :
               cmddata = (CM_REGISTEREDCMDDATA *) Message.LParam ;
               switch (cmddata->wCmd)
               {
                 case CMD_NEXT_KEYWORD :
                      KeywordExpansion (Editor, false, false) ;
                      break ;

                 case CMD_PREV_KEYWORD :
                      KeywordExpansion (Editor, true, false) ;
                      break ;
               }
               Message.Result = true ;
               return ;

          case CMN_CODELISTPOSTCREATE :
               SelectClosestItem (CodeListWindow, NULL) ;
               Message.Result = true ;
               return ;

          case CMN_KEYDOWN :
               keydata = (CM_KEYDATA *) Message.LParam ;
               if ((keydata->nKeyModifier & ~CM_KEY_SHIFT) != 0)
                 break ;
               if (CodeListWindow == NULL)
               {
                 if (ec.TabKeywordExpansion && keydata->nKeyCode == VK_TAB)
                 {
                   IgnoreNext = Message.Result = KeywordExpansion (Editor, keydata->nKeyModifier == CM_KEY_SHIFT, true) ;
                   return ;
                 }
               }
               break ;

          case CMN_KEYPRESS :
               if (IgnoreNext)
               {
                 IgnoreNext = false ;
                 Message.Result = true ;
                 return ;
               }
               keydata = (CM_KEYDATA *) Message.LParam ;
               if ((keydata->nKeyModifier & ~CM_KEY_SHIFT) != 0)
                 break ;
               if (CodeListWindow != NULL && isprint (keydata->nKeyCode))
               {
                 str [0] = (char) keydata->nKeyCode ;
                 str [1] = '\0' ;
                 SelectClosestItem (CodeListWindow, Editor->CurrentWord + str) ;
               }
               break ;

          case CMN_CODELIST :
               if (SingleKeywordMatch (Editor, Editor->CurrentWord.c_str ()))
               {
                 Message.Result = false ;
                 return ;
               }
               codelist = (CM_CODELISTDATA *) Message.LParam ;
               memset (&item, 0, sizeof (item)) ;
               item.mask = LVIF_TEXT ;
               item.iItem = 0 ;
               for (int i = 0 ; i < KeywordCount ; i++)
               {
                 item.pszText = Keywords [i] ;
                 ListView_InsertItem (codelist->hListCtrl, &item) ;
               }
               SelectClosestItem (codelist->hListCtrl, Editor->CurrentWord.c_str ()) ;
               CodeListWindow = codelist->hListCtrl ;
               IgnoreNext = true ;
               Message.Result = true ;
               return ;

          case CMN_CODELISTSELMADE :
               codelist = (CM_CODELISTDATA *) Message.LParam ;
               ListView_GetItemText (codelist->hListCtrl, ListView_GetNextItem (codelist->hListCtrl, -1, LVIS_SELECTED), 0, str, sizeof (str)) ;
               ActionSelection (Editor, str) ;
               CodeListWindow = NULL ;
               Message.Result = false ;
               return ;

          case CMN_CODELISTCANCEL :
               CodeListWindow = NULL ;
               Message.Result = false ;
               return ;

          case CMN_PROPSCHANGE :
               StoreEditor (Editor) ;
               Editor->SetFontOwnership (false) ;
               for (int i = 0 ; i < EditorCount ; i++)
                 if (Editors [i] != Editor)
                   SetupEditor (Editors [i], true) ;
               miConstrainCaret->Checked = ec.SelBoundsEnabled ;
               if (ec.SelBoundsEnabled)
               {
                 miCursorBeyondEOL->Checked = false ;
                 miCursorBeyondEOL->Enabled = false ;
               }
               else
                 miCursorBeyondEOL->Enabled = true ;
               ParentForm->SetMenuShortcuts () ;
               break ;

          case CMN_MODIFIEDCHANGE :
               if (Editor != NULL)
                 Editor->ClearErrorLine () ;
               SendMessage (StatusWindow, SB_SETTEXT, StatusModified, (LPARAM) (Editor->Modified ? "\tMod" : "")) ;
               SendMessage (NotifyWindow, WM_COMMAND, NotifyBase + NotifyModifiedChange, Editor->Modified) ;
               break ;

          case CMN_SELCHANGE :
               Editor->GetSel (&range, false) ;
               if (ExpansionAnchorCol != -1)
                 if (range.posStart.nCol != ExpansionLastCol || range.posStart.nLine != ExpansionAnchorLine)
                   ExpansionAnchorCol = ExpansionAnchorLine = -1 ;
               wsprintf (str, "\tL:%d", LastLine = ++range.posEnd.nLine) ;
               if (!miCursorBeyondEOL->Checked)
               {
                 len = Editor->LineLength [LastLine] ;
                 if (range.posEnd.nCol > len)
                 {
                   if (Editor->LButtonDown)
                     Editor->Col = len + 1 ;
                   return ;
                 }
               }
               SendMessage (StatusWindow, SB_SETTEXT, StatusLine, (LPARAM) str) ;
               wsprintf (str, "\tC:%d", LastCol = ++range.posEnd.nCol) ;
               SendMessage (StatusWindow, SB_SETTEXT, StatusCol, (LPARAM) str) ;
               break ;

          case NM_RCLICK :
               GetCursorPos (&point) ;
               // we can't use PopupMenu->Popup here since it refuses to give us access to
               // the WM_MENUSELECT messages (it uses a private, hidden window for the loop).
               PopupMenu->WindowHandle = MainWindow ;
               PopupMenuPopup (this) ;
               TrackPopupMenuEx (PopupMenu->Handle, 0, point.x, point.y, MainWindow, NULL) ;
               Message.Result = true ;
               return ;
        }
      }
      OldEditParentWndProc (Message) ;
      return ;
    }
  }
  OldEditParentWndProc (Message) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::TabWndProc (Messages::TMessage& Message)
{
  if (Editor != NULL)
  {
    // this makes sure that the tab control itself doesn't keep focus
    // (thus stopping the editor from getting keystrokes).
    if (Message.Msg == WM_SETFOCUS)
    {
      ::SetFocus (Editor->Handle) ;
      Message.Result = 0 ;
      return ;
    }
  }
  OldTabWndProc (Message) ;
}

//---------------------------------------------------------------------------
bool AskFileName (EditTagStruct *t)
{
  char                  szFile [MAX_PATH] ;
  AnsiString            str = GetFilePath (t->LongName) ;
  EditTagStruct         tag ;
  OPENFILENAME          ofn ;

  if (str == "")
    str = InitialDir ;

  ZeroMemory (&ofn, sizeof (OPENFILENAME)) ;
  ofn.lStructSize = sizeof (OPENFILENAME) ;
  strcpy (szFile, t->ShortName.c_str ()) ;
  ofn.hwndOwner = MainWindow ;
  ofn.lpstrFile = szFile ;
  ofn.nMaxFile = sizeof (szFile) ;
  ofn.lpstrFilter = "POV-Ray Files (*.pov;*.inc;*.ini)\0*.pov;*.inc;*.ini\0"
                    "POV-Ray Source (*.pov)\0*.pov\0"
                    "Include Files (*.inc)\0*.inc\0"
                    "INI files (*.ini)\0*.ini\0"
                    "Text Files (*.txt)\0*.txt\0"
                    "All Files (*.*)\0*.*\0" ;
  ofn.nFilterIndex = 1 ;
  ofn.lpstrFileTitle = NULL ;
  ofn.nMaxFileTitle = 0 ;
  ofn.lpstrInitialDir = str.c_str () ;
  ofn.lpstrDefExt = "pov" ;
  ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR | OFN_OVERWRITEPROMPT ;

  SaveDialogActive = true ;
  while (1)
  {
    if (GetSaveFileName (&ofn) != 0)
    {
      MakeFileNames (&tag, szFile) ;
      if (FindEditorIndex (tag.LongName) != -1)
      {
        ShowMessage ("This file is already open in the editor") ;
        continue ;
      }
      t->LongName = tag.LongName ;
      t->ShortName = tag.ShortName ;
      InitialDir = GetFilePath (szFile) ;
      SaveDialogActive = false ;
      return (true) ;
    }
    SaveDialogActive = false ;
    return (false) ;
  }
}

//---------------------------------------------------------------------------
TSaveType SaveEditorFile (TCodeMax *e)
{
  AnsiString            str ;
  EditTagStruct         *t = (EditTagStruct *) e->Tag ;
  EditTagStruct         tag = *t ;

  PutStatusMessage ("") ;
  e->ClearErrorLine () ;
  if (t->LongName == "")
    if (!AskFileName (&tag))
      return (stCancel) ;
  if (CreateBackups && !e->BackedUp)
  {
    if (FileExists (t->LongName))
    {
      str = t->LongName + ".bak" ;
      if (FileExists (str))
        if (!DeleteFile (str))
          ShowErrorMessage (MakeBaseName (str), "Failed to delete old backup file") ;
      if (!RenameFile (t->LongName, str))
        ShowErrorMessage (t->ShortName, "Failed to rename original file to .bak") ;
      else
        e->BackedUp = true ;
    }
  }
  if (e->SaveFile (tag.LongName, !UndoAfterSave) == CME_SUCCESS)
  {
    UpdateFileTime (e) ;
    if (t->LongName != tag.LongName)
    {
      *t = tag ;
      AddToRecent (t->LongName) ;
      e->FileName = t->LongName ;
      ParentForm->Tab->Tabs->Strings [e->Index] = t->ShortName ;
      SetLanguageBasedOnFileType (e, t->ShortName) ;
      UpdateRecent () ;
    }
    PutStatusMessage ("File saved") ;
    return (stSaved) ;
  }
  ShowErrorMessage (t->ShortName, "Failed to save file") ;
  return (stError) ;
}

//---------------------------------------------------------------------------
TSaveType TrySave (TCodeMax *e)
{
  EditTagStruct         *t = (EditTagStruct *) e->Tag ;

  switch (SaveEditorFile (e))
  {
    case stSaved :
         return (stSaved) ;

    case stCancel :
         return (stCancel) ;

    default :
         switch (SaveFailedForm->ShowModal (t->ShortName))
         {
           case mrAbort :
                return (stCancel) ;

           case mrRetry :
                return (stRetry) ;

           case mrIgnore :
                return (stDiscard) ;

           case mrCancel :
                return (stCancel) ;
         }
         return (stDiscard) ;
  }
}

//---------------------------------------------------------------------------
TCodeMax *TParentForm::CreateNewEditor (char *FileName, bool ReadOnly, bool ShowIt)
{
  int                   lastError ;
  TCodeMax              *c ;
  AnsiString            fullname ;
  struct stat           st ;
  EditTagStruct         *t ;

  if (FileName != NULL)
  {
    fullname = GetFullPath (AnsiString (FileName)) ;
    if (FindEditorIndex (fullname.c_str ()) != -1)
    {
      SelectFile (fullname.c_str ()) ;
      return (NULL) ;
    }
  }
  if (EditorCount == MAX_EDITORS)
  {
    ShowMessage ("Maximum number of editing sessions reached") ;
    return (NULL) ;
  }
  try
  {
    c = new TCodeMax (EditPanel) ;
    t = new EditTagStruct ;
  }
  catch (Exception& e)
  {
    return (NULL) ;
  }
  c->Parent = EditPanel ;
  c->Align = alClient ;
  memset (t, 0, sizeof (EditTagStruct)) ;
  c->Tag = (int) t ;
  c->ReadOnly = ReadOnly ;
  c->GlobalPropsEnabled = false ;
  c->OnKeyUp = ParentForm->CodeMaxKeyUp ;
  c->OnKeyDown = ParentForm->CodeMaxKeyDown ;
  if (FileName)
  {
    MakeFileNames (t, FileName) ;
    c->FileName = t->LongName ;
    if (c->OpenFile (t->LongName) != CME_SUCCESS)
    {
      lastError = GetLastError () ;
      // CodeMax doesn't seem to like opening zero-length files.
      // so we check for ERROR_FILE_INVALID for a file with length 0
      // it's OK if that's the case
      if (lastError != ERROR_FILE_INVALID || stat (t->LongName.c_str (), &st) != 0 || st.st_size > 0)
      {
        // file not found is OK, too
        if (lastError != ERROR_FILE_NOT_FOUND)
        {
          ShowErrorMessage (t->ShortName, "Failed to open file", lastError) ;
          delete t ;
          delete c ;
          return (NULL) ;
        }
        else
          PutStatusMessage (AnsiString ("File '") + t->LongName + "' not found ; new file assumed") ;
      }
      else
        PutStatusMessage (AnsiString ("File '") + t->LongName + "' has zero length") ;
    }
    UpdateFileTime (c) ;
  }
  else
    t->ShortName = "Untitled" ;
  SetLanguageBasedOnFileType (c, t->ShortName) ;
  Editors [EditorCount] = c ;
  c->Index = EditorCount + 1 ;
  Editor = Editors [EditorCount++] ;
  SetupEditor (c, false) ;
  Tab->Tabs->Append (t->ShortName) ;
  if (ShowIt)
  {
    Tab->TabIndex = EditorCount + EditorTabOffset ;
    TabChange (Tab) ;
  }
  c->Visible = ShowIt ;
  StoreEditor (Editor) ;
  return (c) ;
}

//---------------------------------------------------------------------------
void __fastcall TCPanel::CreateParams (TCreateParams &Params)
{
  TPanel::CreateParams (Params) ;
  Params.Style = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS ;
  Params.WindowClass.hbrBackground = NULL ;
  Params.X = Params.Y = 0 ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::CreateParams (TCreateParams &Params)
{
  TForm::CreateParams (Params) ;
  if (MainWindow)
  {
    Params.WndParent = MainWindow ;
    Params.Style = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS ;
    Params.X = Params.Y = 0 ;
  }
}

//---------------------------------------------------------------------------
__fastcall TParentForm::TParentForm(TComponent* Owner) : TForm(Owner)
{
  CodeListWindow = NULL ;
  IgnoreNext = false ;
  ExpansionWord = "" ;
  ExpansionAnchorCol = -1 ;
  ExpansionAnchorLine = -1 ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::MessagePanelResize(TObject *Sender)
{
  if (MessageWindow == NULL)
    return ;
  MoveWindow (MessageWindow, 0, 0, MessagePanel->Width, MessagePanel->Height, TRUE) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::TabChange(TObject *Sender)
{
  int         TabIndex = Tab->TabIndex ;

  if (TabIndex >= Tab->Tabs->Count || TabIndex == -1)
    Tab->TabIndex = TabIndex = Tab->Tabs->Count - 1 ;
  if (TabIndex > EditorTabOffset)
  {
    Editor = Editors [--TabIndex - EditorTabOffset] ;
    if (EditDragPanelVisible)
      ShowEditDragPanel (true) ;
    EditPanel->BringToFront () ;
    Editor->Visible = true ;
    Editor->BringToFront () ;
    for (int i = 0 ; i < EditorCount ; i++)
      if (Editors [i] != Editor)
        Editors [i]->Visible = false ;
    if (IsWindowVisible (MainWindow))
      if (Editor->CanFocus ())
        Editor->SetFocus () ;
    StoreEditor (Editor) ;
  }
  else if (TabIndex == 0)
  {
    if (EditDragPanelVisible)
      ShowEditDragPanel (false) ;
    MessagePanel->BringToFront () ;
    ::SetFocus (MessageWindow) ;
    Editor = NULL ;
  }
  else
  {
    ShowMessage ("Internal editor error: unknown tab destination") ;
    Tab->TabIndex = TabIndex = 0 ;
    if (EditDragPanelVisible)
      ShowEditDragPanel (false) ;
    MessagePanel->BringToFront () ;
    ::SetFocus (MessageWindow) ;
    Editor = NULL ;
  }
  SetStatusLine () ;
  SetMenuState () ;
  SendMessage (NotifyWindow, WM_COMMAND, NotifyBase + NotifyTabChange, GetFlags ()) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::FormCreate(TObject *Sender)
{
  TMenuItem             *m ;

  miSaveAs->ShortCut = ShortCut (Word ('A'), TShiftState () << ssCtrl << ssShift) ;
  miSaveAll->ShortCut = ShortCut (Word ('V'), TShiftState () << ssCtrl << ssShift) ;
  miExit->ShortCut = ShortCut (Word ('X'), TShiftState () << ssAlt) ;
  miShowMessages->ShortCut = ShortCut (Word ('M'), TShiftState () << ssAlt) ;
  pmiShowMessages->ShortCut = ShortCut (Word ('M'), TShiftState () << ssAlt) ;
  pmiSaveAs->ShortCut = ShortCut (Word ('A'), TShiftState () << ssCtrl << ssShift) ;
  pmiSaveAll->ShortCut = ShortCut (Word ('V'), TShiftState () << ssCtrl << ssShift) ;
//pmiOpenFileUnderCursor->ShortCut = ShortCut (Word ('G'), TShiftState () << ssCtrl << ssShift) ;
  for (int i = 0 ; i < 9 ; i++)
  {
    m = new TMenuItem (miFileMenu) ;
    m->OnClick = RecentMenuClick ;
    m->Tag = i ;
    miFileMenu->Add (m) ;
    m->Visible = false ;
    RecentMenuItems [i] = m ;
  }
  for (int i = 0 ; i < MAX_OLDER_FILES ; i++)
  {
    m = new TMenuItem (miOlderFiles) ;
    m->OnClick = OlderMenuClick ;
    m->Tag = i ;
    miOlderFiles->Add (m) ;
    m->Visible = false ;
    OlderMenuItems [i] = m ;
  }
}

//---------------------------------------------------------------------------
HWND TParentForm::Initialise (void)
{
  EditPanel = new TCPanel (Tab) ;
  EditPanel->Parent = Tab ;
  EditPanel->Align = alClient ;
  EditPanel->Visible = true ;
  OldTabWndProc = Tab->WindowProc ;
  Tab->WindowProc = TabWndProc ;
  TabClientTop = HiddenPanel->Top ;
  TabClientWidth = HiddenPanel->Width ;
  TabXBorder = HiddenPanel->Left ;
  TabYBorder = Tab->Height - EditPanel->Height - TabClientTop ;
  EditDragPanel->Left = TabXBorder ;
  EditDragPanel->Top = Tab->Height - EditDragOffset ;
  EditDragPanel->Width = TabClientWidth ;
  OldEditParentWndProc = EditPanel->WindowProc ;
  EditPanel->WindowProc = EditParentWndProc ;
  BuildDirList (miInsertMenu, InsertPath) ;
  new InsertChangeThread (InsertPath) ;
  return (MessagePanel->Handle) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::OnHint (TObject *Sender)
{
  PutStatusMessage (GetLongHint (Application->Hint)) ;
}

//---------------------------------------------------------------------------
// DLL INTERFACE CODE
//
// As long as POV-Ray itself is the only process that services the VCL's
// message loop, there is no danger of re-rentering the VCL. Hence we do
// not need to use Synchronize () or similar methods of sorting things out.
// What we do do, however, is to ensure access to the methods here are
// serialised using a semaphore.
//
//---------------------------------------------------------------------------
HWND WINAPI CreateTabWindow (HWND ParentWindow, HWND StatusWindow, char *HomePath)
{
  TLock                 lock ;

  try
  {
    ::HomePath = HomePath ;
    ::StatusWindow = StatusWindow ;
    InsertPath = FixPath (::HomePath + "Insert Menu") ;
    POVRayIniPath = FixPath (::HomePath + "renderer\\povray.ini") ;
    RecentFiles = new TStringList ;
    OlderFiles = new TStringList ;
    Application->Title = "POV-Ray Editor" ;
    Application->OnHint = ParentForm->OnHint ;
    MainWindow = ParentWindow ;
    Application->CreateForm (__classid (TParentForm), &ParentForm) ;
    Application->CreateForm (__classid (TAskSaveForm), &AskSaveForm) ;
    Application->CreateForm (__classid (TSaveFailedForm), &SaveFailedForm) ;
    Application->CreateForm (__classid (TRenderSaveForm), &RenderSaveForm) ;
    Application->CreateForm (__classid (TGenericInputForm), &GenericInputForm) ;
    Application->CreateForm (__classid (TPrintForm), &PrintForm) ;
    return (ParentForm->Initialise ()) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return (NULL) ;
  }
}

//---------------------------------------------------------------------------
DWORD WINAPI GetDLLVersion (void)
{
  return (EDITDLLVERSION) ;
}

//---------------------------------------------------------------------------
int CompareKeywords (const void *p1, const void *p2)
{
  return (strcmp (*((char **) p1), *((char **) p2))) ;
}

//---------------------------------------------------------------------------
void WINAPI SetKeywords (LPCSTR KeywordList)
{
  LangPOVRay.pszKeywords = KeywordList ;
  TCodeMax::RegisterLanguage ("POV-Ray", &LangPOVRay) ;
  LangPOVRay.pszKeywords = NULL ;
  char *str = strdup (KeywordList) ;
  char *s = strtok (str, "\n") ;
  while (s != NULL && KeywordCount < MAX_KEYWORDS)
  {
    if (strlen (s) > 1)
      Keywords [KeywordCount++] = s ;
    s = strtok (NULL, "\n") ;
  }
  // we don't free str - it's still being used.
  qsort (Keywords, KeywordCount, sizeof (char *), CompareKeywords) ;
}

//---------------------------------------------------------------------------
void WINAPI SetWindowPosition (int x, int y, int w, int h)
{
  TLock                 lock ;

  if (ParentForm == NULL)
    return ;
  try
  {
    ParentForm->SetBounds (x, y, w, h) ;
    ParentForm->Visible = true ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return ;
  }
}

//---------------------------------------------------------------------------
void WINAPI SetMessageWindow (HWND MsgWindow)
{
  TLock                 lock ;

  try
  {
    MessageWindow = MsgWindow ;
    MoveWindow (MessageWindow, 0, 0, ParentForm->MessagePanel->Width, ParentForm->MessagePanel->Height, TRUE) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return ;
  }
}

//---------------------------------------------------------------------------
void WINAPI RestoreState (int RestoreFiles)
{
  TLock                 lock ;

  try
  {
    TRegDef *r = new TRegDef ("\\Software\\" REGKEY "\\" REGVERKEY "\\POV-Edit") ;
    ParentForm->GetSettings (r, RestoreFiles) ;
    delete r ;
    SetupEditor (NULL, false) ;
    ParentForm->TabChange (ParentForm->Tab) ;
    MakeRecentMenus () ;
    MakeOlderMenus () ;
    ParentForm->SetMenuShortcuts () ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return ;
  }
}

//---------------------------------------------------------------------------
void WINAPI SaveState (void)
{
  TLock                 lock ;

  try
  {
    TRegDef *r = new TRegDef ("\\Software\\" REGKEY "\\" REGVERKEY "\\POV-Edit") ;
    ParentForm->PutSettings (r) ;
    delete r ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return ;
  }
}

//---------------------------------------------------------------------------
bool WINAPI SelectFile (char *FileName)
{
  int                   index ;
  TLock                 lock ;

  try
  {
    if (FileName == NULL)
      return (false) ;
    if ((index = FindEditorIndex (GetFullPath (FileName))) == -1)
      return (false) ;
    ParentForm->Tab->TabIndex = index + EditorTabOffset + 1 ;
    ParentForm->TabChange (ParentForm->Tab) ;
    return (true) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return (false) ;
  }
}

//---------------------------------------------------------------------------
// Currently we don't support not making a new window - it's always made.
//---------------------------------------------------------------------------
bool WINAPI BrowseFile (bool CreateNewWindow)
{
  int                   ok = 0 ;
  char                  *s ;
  TLock                 lock ;
  AnsiString            fileName ;
  OPENFILENAME          ofn ;
  static char           szFile [8192] = "" ;

  ZeroMemory (&ofn, sizeof (ofn)) ;
  ofn.lStructSize = sizeof (ofn) ;
  ofn.hwndOwner = MainWindow ;
  ofn.lpstrFile = szFile ;
  ofn.nMaxFile = sizeof (szFile) ;
  ofn.lpstrFilter = "POV-Ray Files (*.pov;*.inc;*.ini)\0*.pov;*.inc;*.ini\0"
                    "POV-Ray Source (*.pov)\0*.pov\0"
                    "Include Files (*.inc)\0*.inc\0"
                    "INI files (*.ini)\0*.ini\0"
                    "Text Files (*.txt)\0*.txt\0"
                    "All Files (*.*)\0*.*\0" ;
  ofn.nFilterIndex = 1 ;
  ofn.lpstrFileTitle = NULL ;
  ofn.nMaxFileTitle = 0 ;
  ofn.lpstrInitialDir = InitialDir.c_str () ;
  ofn.lpstrDefExt = "pov" ;
  ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR | OFN_EXTENSIONDIFFERENT ;
  ofn.Flags |= OFN_CREATEPROMPT | OFN_ALLOWMULTISELECT | OFN_EXPLORER ;

  try
  {
    if (GetOpenFileName ((struct tagOFNA *) &ofn) != 0)
    {
      s = szFile + ofn.nFileOffset ;
      InitialDir = szFile ;
      if (!ofn.nFileOffset || szFile [ofn.nFileOffset - 1])
        InitialDir = GetFilePath (InitialDir.c_str ()) ;
      while (*s)
      {
        fileName = InitialDir + "\\" + s ;
        // skip up to and over the next '\0'
        while (*s++) ;
        if (FindEditorIndex (fileName.c_str ()) != -1)
        {
          SelectFile (fileName.c_str ()) ;
          continue ;
        }
        if (ParentForm->CreateNewEditor (fileName.c_str (), false, false) == NULL)
          break ;
        AddToRecent (fileName) ;
        ok++ ;
      }
    }
    if (ok)
    {
      ParentForm->Tab->TabIndex = EditorCount + EditorTabOffset ;
      ParentForm->TabChange (ParentForm->Tab) ;
    }
    return (ok != 0) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return (false) ;
  }
}

//---------------------------------------------------------------------------
// NULL means create a new, untitled file
//---------------------------------------------------------------------------
bool WINAPI LoadFile (char *FileName)
{
  bool                  result ;
  TLock                 lock ;

  try
  {
    result = ParentForm->CreateNewEditor (FileName) != NULL ;
    if (result && FileName != NULL)
      AddToRecent (FileName) ;
    return (result) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return (false) ;
  }
}

//---------------------------------------------------------------------------
bool WINAPI CloseFile (char *FileName)
{
  int                   index ;
  TLock                 lock ;
  TCodeMax              *e ;
  TSaveType             result ;
  EditTagStruct         *t ;

  try
  {
    UpdateRecent () ;
    index = ParentForm->Tab->TabIndex - EditorTabOffset - 1 ;
    if (FileName != NULL)
      index = FindEditorIndex (GetFullPath (FileName)) ;
    if (index != -1)
    {
      e = Editors [index] ;
      t = (EditTagStruct *) e->Tag ;
      if (e->Modified)
      {
        switch (AskSaveForm->ShowModal (t->ShortName, false))
        {
          case mrAll :
          case mrYes :
               while ((result = TrySave (e)) == stRetry) ;
               if (result == stCancel)
                 return (false) ;
               break ;

          case mrNo :
               break ;

          case mrCancel :
               return (false) ;
        }
      }
      if (e == Editor)
        Editor = NULL ;
      delete (EditTagStruct *) e->Tag ;
      delete e ;
      memcpy (Editors + index, Editors + index + 1, (--EditorCount - index) * sizeof (e)) ;
      for (int i = 0 ; i < EditorCount ; i++)
        Editors [i]->Index = i + 1 ;
      ParentForm->Tab->Tabs->Delete (index + 1) ;
      ParentForm->Tab->TabIndex = index + EditorTabOffset + 1 ;
      ParentForm->TabChange (ParentForm->Tab) ;
      return (true) ;
    }
    return (false) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return (false) ;
  }
}

//---------------------------------------------------------------------------
bool WINAPI ExternalLoadFile (char *ParamString)
{
  int                   Line ;
  TLock                 lock ;
  TCodeMax              *e ;
  AnsiString            params = ParamString ;
  AnsiString            FileName ;

  try
  {
    if (params == "" || params [1] == ',')
      return (false) ;
    FileName = GetFullPath (GetNextField (params)) ;
    e = FindEditor (FileName.c_str ()) ;
    if (e == NULL)
      if ((e = ParentForm->CreateNewEditor (FileName.c_str (), false, false)) == NULL)
        return (false) ;
    SelectFile (FileName.c_str ()) ;
    try
    {
      if (params != "")
        e->Line = Line = GetNextField (params).ToInt () ;
      if (params != "")
        e->Col = GetNextField (params).ToInt () ;
      if (params != "")
      {
        e->TopLine = GetNextField (params).ToInt () ;
        e->Line = Line ;
      }
      if (params != "")
        e->Language = (TLanguage) GetNextField (params).ToInt () ;
    }
    catch (EConvertError& E)
    {
      return (false) ;
    }
    AddToRecent (FileName) ;
    SetStatusLine () ;
    return (true) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return (false) ;
  }
}

//---------------------------------------------------------------------------
bool WINAPI SaveFile (char *FileName)
{
  TLock                 lock ;
  TCodeMax              *e = Editor ;
  TSaveType             result = stDiscard ;

  try
  {
    if (FileName != NULL)
      e = FindEditor (GetFullPath (FileName)) ;
    if (e != NULL)
      while ((result = TrySave (e)) == stRetry) ;
    return (result == stSaved) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return (false) ;
  }
}

//---------------------------------------------------------------------------
DWORD WINAPI GetTab (void)
{
  TLock                 lock ;

  try
  {
    return (ParentForm->Tab->TabIndex) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return (0) ;
  }
}

//---------------------------------------------------------------------------
DWORD WINAPI GetFlags (void)
{
  DWORD                 flags = 0 ;
  TLock                 lock ;

  try
  {
    if (Editor != NULL)
    {
      if (Editor->Modified)
        flags |= EDIT_CURRENT_MODIFIED ;
      if (Editor->CanUndo)
        flags |= EDIT_CAN_UNDO ;
      if (Editor->CanRedo)
        flags |= EDIT_CAN_REDO ;
      if (!Editor->ReadOnly && Editor->FileName != "")
        flags |= EDIT_CAN_WRITE ;
    }
    else
      flags |= EDIT_MSG_SELECTED ;
    for (int i = 0 ; i < EditorCount ; i++)
    {
      if (Editors [i]->Modified)
      {
        flags |= EDIT_ANY_MODIFIED ;
        break ;
      }
    }
    if (EditorCount < MAX_EDITORS)
      flags |= EDIT_CAN_OPEN ;
    if (EditorCount > 0)
      flags |= EDIT_CAN_CLOSE ;
    return (flags) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return (0) ;
  }
}

//---------------------------------------------------------------------------
char *WINAPI GetFilename (void)
{
  TLock                 lock ;
  static char str [MAX_PATH] ;

  try
  {
    if (Editor == NULL)
      return (NULL) ;
    strcpy (str, Editor->FileName.c_str ()) ;
    return (str) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return (NULL) ;
  }
}

//---------------------------------------------------------------------------
void WINAPI NextTab (bool Forward)
{
  TLock                 lock ;

  try
  {
    TTabControl *tab = ParentForm->Tab ;
    if (Forward)
      tab->TabIndex = tab->TabIndex < EditorCount + EditorTabOffset ? tab->TabIndex + 1 : 0 ;
    else
      tab->TabIndex = tab->TabIndex > 0 ? tab->TabIndex - 1 : EditorCount + EditorTabOffset ;
    ParentForm->TabChange (tab) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return ;
  }
}

//---------------------------------------------------------------------------
bool CloseAll (void)
{
  bool                  saveAll = false ;
  bool                  cancel = false ;
  bool                  discard ;
  TCodeMax              *e ;
  TSaveType             result ;
  EditTagStruct         *t ;

  UpdateRecent () ;
  for (int i = EditorCount - 1 ; i >= 0 && !cancel ; i--)
  {
    e = Editors [i] ;
    if (e->Modified)
    {
      discard = false ;
      if (!saveAll)
      {
        t = (EditTagStruct *) e->Tag ;
        switch (AskSaveForm->ShowModal (t->ShortName, true))
        {
          case mrAll :
               saveAll = true ;
               break ;

          case mrYes :
               break ;

          case mrNo :
               discard++ ;
               break ;

          case mrCancel :
               cancel++ ;
               continue ;
        }
      }
      if (!discard)
      {
        while ((result = TrySave (e)) == stRetry) ;
        if (result == stCancel)
        {
          cancel++ ;
          continue ;
        }
      }
    }
    if (e == Editor)
      Editor = NULL ;
    delete (EditTagStruct *) e->Tag ;
    delete e ;
    memcpy (Editors + i, Editors + i + 1, (--EditorCount - i) * sizeof (e)) ;
    ParentForm->Tab->Tabs->Delete (i + 1) ;
  }
  ParentForm->TabChange (ParentForm->Tab) ;
  return (!cancel) ;
}

//---------------------------------------------------------------------------
bool SaveAllFiles (bool IncludeUntitled)
{
  TCodeMax              *e ;
  TSaveType             result ;

  UpdateRecent () ;
  for (int i = 0 ; i < EditorCount ; i++)
  {
    e = Editors [i] ;
    if (e->FileName == "")
      if (!IncludeUntitled || SaveDialogActive)
        continue ;
    if (e->Modified)
    {
      while ((result = TrySave (e)) == stRetry) ;
      if (result == stCancel)
        return (false) ;
    }
  }
  SetStatusLine () ;
  return (true) ;
}

//---------------------------------------------------------------------------
bool WINAPI CanClose (bool AllFiles)
{
  bool                  saveAll = false ;
  TLock                 lock ;
  TCodeMax              **e = Editors ;
  TSaveType             result ;
  EditTagStruct         *t ;

  try
  {
    UpdateRecent () ;
    if (AllFiles)
    {
      for (int i = 0 ; i < EditorCount ; i++, e++)
      {
        if ((*e)->Modified)
        {
          if (!saveAll)
          {
            t = (EditTagStruct *) (*e)->Tag ;
            switch (AskSaveForm->ShowModal (t->ShortName, true))
            {
              case mrAll :
                   saveAll = true ;
                   break ;

              case mrYes :
                   break ;

              case mrNo :
                   if (AskSaveForm->DiscardAll)
                     return (true) ;
                   continue ;

              case mrCancel :
                   return (false) ;
            }
          }
          while ((result = TrySave (*e)) == stRetry) ;
          if (result == stCancel)
            return (false) ;
        }
      }
    }
    else
    {
      if (Editor != NULL && Editor->Modified)
      {
        if (AskSaveForm->ShowModal (((EditTagStruct *) Editor->Tag)->ShortName) == mrYes)
        {
          while ((result = TrySave (Editor)) == stRetry) ;
          if (result == stCancel)
            return (false) ;
        }
      }
    }
    return (true) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return (true) ;
  }
}

//---------------------------------------------------------------------------
// Save any modified files so a render can proceed. Ask the user, but give
// them an option to make the save automatic in future (this session only).
// Returns FALSE if the render should not proceed.
//---------------------------------------------------------------------------
bool WINAPI SaveModified (char *FileName)
{
  bool                  saveAll ;
  TLock                 lock ;
  TCodeMax              **e = Editors ;
  TCodeMax              *se = NULL ;
  TSaveType             result ;
  EditTagStruct         *t ;

  try
  {
    UpdateRecent () ;
    saveAll = RenderSaveForm->DontAsk->Checked ;
    if (FileName != NULL)
      se = FindEditor (GetFullPath (FileName)) ;
    for (int i = 0 ; i < EditorCount ; i++, e++)
    {
      if ((*e)->Modified)
      {
        if (!saveAll)
        {
          if (se != NULL && *e != se)
            continue ;
          t = (EditTagStruct *) (*e)->Tag ;
          RenderSaveForm->MessageLabel->Caption = AnsiString ("File '") + t->ShortName + "' has changed - save ?" ;
          switch (RenderSaveForm->ShowModal ())
          {
            case mrAll :
                 saveAll = true ;
                 break ;

            case mrYes :
                 saveAll = RenderSaveForm->DontAsk->Checked ;
                 break ;

            case mrNo :
                 continue ;

            case mrCancel :
                 return (false) ;
          }
        }
        while ((result = TrySave (*e)) == stRetry) ;
        if (result == stCancel)
          return (false) ;
      }
    }
    return (true) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return (false) ;
  }
}

//---------------------------------------------------------------------------
void WINAPI ShowMessages (bool on)
{
  TLock                 lock ;

  try
  {
    if (!ParentForm->miShowParseMessages->Checked)
      return ;
    EditDragPanelVisible = on ;
    if (Editor != NULL)
      ParentForm->ShowEditDragPanel (on) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return ;
  }
}

//---------------------------------------------------------------------------
void WINAPI DispatchMenuId (DWORD id)
{
  TLock                 lock ;

  try
  {
    if (!ParentForm->Menus->DispatchCommand ((WORD) id))
      ParentForm->PopupMenu->DispatchCommand ((WORD) id) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return ;
  }
}

//---------------------------------------------------------------------------
bool WINAPI PassOnMessage (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, DWORD *rVal)
{
  TLock                 lock ;
  TMenuItem             *e ;

  ClearBMPHint (message, wParam, lParam) ;
  if (ParentForm == NULL)
    return (false) ;
  switch (message)
  {
    case WM_MENUSELECT :
    case WM_INITMENU :
    case WM_INITMENUPOPUP :
    case WM_ACTIVATEAPP :
    case WM_CHAR :
    case WM_KEYDOWN :
    case HIDE_NEWUSER_HELP_MESSAGE :
         break ;

    default :
         return (0) ;
  }

  try
  {
    switch (message)
    {
      case HIDE_NEWUSER_HELP_MESSAGE :
           ParentForm->miFileMenuHelp->Visible = !wParam ;
           ParentForm->miFileMenuHelpSeparator->Visible = !wParam ;
           ParentForm->miEditMenuHelp->Visible = !wParam ;
           ParentForm->miEditMenuHelpSeparator->Visible = !wParam ;
           ParentForm->miSearchMenuHelp->Visible = !wParam ;
           ParentForm->miSearchMenuHelpSeparator->Visible = !wParam ;
           ParentForm->miTextMenuHelp->Visible = !wParam ;
           ParentForm->miTextMenuHelpSeparator->Visible = !wParam ;
           ParentForm->miEditorMenuHelp->Visible = !wParam ;
           ParentForm->miEditorMenuHelpSeparator->Visible = !wParam ;
           ParentForm->miInsertMenuHelp->Visible = !wParam ;
           return (true) ;

      case WM_MENUSELECT :
           ClearBMPHint () ;
           if (LOWORD (wParam) == 0)
             break ;
           if ((HIWORD (wParam) & MF_POPUP) == 0)
           {
             if ((e = ParentForm->Menus->FindItem (LOWORD (wParam), fkCommand)) != NULL)
             {
               if (e->Hint.SubString (1, 9) == "Show BMP ")
                 ShowBMPHint (e) ;
               else
                 PutStatusMessage (e->Hint) ;
               *rVal = 0 ;
               return (true) ;
             }
             if ((e = ParentForm->PopupMenu->FindItem (LOWORD (wParam), fkCommand)) != NULL)
             {
               PutStatusMessage (e->Hint) ;
               *rVal = 0 ;
               return (true) ;
             }
           }
           break ;

      case WM_INITMENU :
      case WM_INITMENUPOPUP :
           if ((e = ParentForm->Menus->FindItem (wParam, fkHandle)) != NULL)
           {
             ParentForm->SetMenuState () ;
             PutStatusMessage (e->Hint) ;
             *rVal = 0 ;
             return (true) ;
           }
           break ;

      case WM_ACTIVATEAPP :
           if (IsWindowVisible (MainWindow) && !IsIconic (MainWindow))
           {
             if (wParam != 0)
               CheckReload () ;
             else if (AutoReload)
               SaveAllFiles (false) ;
           }
           break ;

      case WM_CHAR :
      case WM_KEYDOWN :
           return (SendMessage (ParentForm->Handle, message, wParam, lParam)) ;
    }
    return (false) ;
  }
  catch (Exception &E)
  {
    return (false) ;
  }
}

//---------------------------------------------------------------------------
void WINAPI SetVisible (bool visible)
{
  TLock                 lock ;

  try
  {
    ParentForm->Visible = visible ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return ;
  }
}

//---------------------------------------------------------------------------
HMENU WINAPI GetMenuHandle (int which)
{
  HMENU                 result ;
  TLock                 lock ;

  try
  {
    switch (which)
    {
      case GetFileMenu :
           result = ParentForm->miFileMenu->Handle ;
           break ;

      case GetEditMenu :
           result = ParentForm->miEditMenu->Handle ;
           break ;

      case GetSearchMenu :
           result = ParentForm->miSearchMenu->Handle ;
           break ;

      case GetTextMenu :
           result = ParentForm->miTextMenu->Handle ;
           break ;

      case GetInsertMenu :
           result = ParentForm->miInsertMenu->Handle ;
           break ;

      case GetOptionsMenu :
           result = ParentForm->miEditorMenu->Handle ;
           break ;

      default :
           result = NULL ;
           break ;
    }
    return (result) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return (NULL) ;
  }
}

//---------------------------------------------------------------------------
void WINAPI UpdateMenus (HMENU MenuHandle)
{
  TLock                 lock ;

  try
  {
    ParentForm->SetMenuState () ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return ;
  }
}

//---------------------------------------------------------------------------
void WINAPI SetNotifyBase (HWND WindowHandle, int MessageBase)
{
  NotifyWindow = WindowHandle ;
  NotifyBase = MessageBase ;
}

//---------------------------------------------------------------------------
void WINAPI GetContextHelp (void)
{
  TLock                 lock ;

  try
  {
    if (Editor != NULL)
    {
      // now that we're no longer using WinHelp, we'll put the HTMLHelp call through the
      // main engine, so it's centralised in one place (plus solves the problem of not
      // having a HTMLHelp .lib suitable for C++Builder).
      SendMessage (MainWindow, KEYWORD_LOOKUP_MESSAGE, 0, (DWORD) GetCurrentKeyword (Editor).c_str ()) ;
    }
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return ;
  }
}

//---------------------------------------------------------------------------
void WINAPI SetTabFocus (void)
{
  TLock                 lock ;

  try
  {
    if (IsWindowVisible (MainWindow))
      SetFocus (Editor != NULL ? Editor->Handle : MessageWindow) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return ;
  }
}

//---------------------------------------------------------------------------
bool WINAPI ShowParseError (char *FileName, char *Message, int Line, int Col)
{
  bool                  isthis = false ;
  TLock                 lock ;
  AnsiString            str (Message) ;
  AnsiString            file (FileName) ;

  try
  {
    if (Editor != NULL)
      if (Editor->FileName.AnsiCompareIC (file) == 0)
        isthis = true ;
    if (!isthis)
    {
      if (!ParentForm->miAutoLoadErrorFile->Checked)
        return (false) ;
      if (!SelectFile (FileName))
        if (ParentForm->CreateNewEditor (FileName, false, true) == NULL)
          return (false) ;
    }
    Editor->Line = Line ;
    Editor->Col = Col ;
    Editor->ErrorLine = Line ;
    PutStatusMessage (str) ;
    ShowMessages (false) ;
    return (true) ;
  }
  catch (Exception &E)
  {
    ShowMessage (AnsiString (E.ClassName ()) + ": " + E.Message) ;
    return (false) ;
  }
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::CodeMaxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
{
  if (Editor == NULL)
    return ;
  if (LastOverwrite != Editor->OvertypeMode)
  {
    LastOverwrite = Editor->OvertypeMode ;
    SendMessage (StatusWindow, SB_SETTEXT, StatusIns, (LPARAM) (LastOverwrite ? "\tOvr" : "\tIns")) ;
  }
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miNewFileClick(TObject *Sender)
{
  LoadFile (NULL) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miOpenFileClick(TObject *Sender)
{
  BrowseFile (true) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miCloseFileClick(TObject *Sender)
{
  CloseFile (NULL) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miCloseAllFilesClick(TObject *Sender)
{
  ::CloseAll () ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miPrintClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  PrintForm->e = Editor ;
  PrintForm->ShowModal () ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miPrintSetupClick(TObject *Sender)
{
  PrintForm->SetupDialog->Execute () ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miExitClick(TObject *Sender)
{
  SendMessage (MainWindow, WM_CLOSE, 0, 0) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miSaveAllClick(TObject *Sender)
{
  SaveAllFiles (true) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miSaveClick(TObject *Sender)
{
  if (Editor != NULL)
    SaveFile (NULL) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miSaveAsClick(TObject *Sender)
{
  EditTagStruct         *t ;
  EditTagStruct         tag ;

  if (Editor == NULL)
    return ;
  t = (EditTagStruct *) Editor->Tag ;
  tag = *t ;
  if (!AskFileName (&tag))
    return ;
  if (Editor->SaveFile (tag.LongName, !UndoAfterSave) == CME_SUCCESS)
  {
    *t = tag ;
    UpdateFileTime (Editor) ;
    AddToRecent (t->LongName) ;
    Editor->FileName = t->LongName ;
    ParentForm->Tab->Tabs->Strings [Editor->Index] = t->ShortName ;
    UpdateRecent () ;
    SetLanguageBasedOnFileType (Editor, t->ShortName) ;
    // pretend the tab has changed so the window caption is updated
    SendMessage (NotifyWindow, WM_COMMAND, NotifyBase + NotifyTabChange, GetFlags ()) ;
  }
  else
    ShowErrorMessage (t->ShortName, "Failed to save file") ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miUndoAfterSaveClick(TObject *Sender)
{
  UndoAfterSave = !UndoAfterSave ;
  miUndoAfterSave->Checked = UndoAfterSave ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miPropertiesClick(TObject *Sender)
{
  if (Editor != NULL)
    Editor->ShowProperties () ;
}

//---------------------------------------------------------------------------
void TParentForm::SetMenuState (void)
{
  bool                  canCut = Editor != NULL && Editor->CanCut ;

  miNewFile->Enabled = EditorCount < MAX_EDITORS ;
  miOpenFile->Enabled = EditorCount < MAX_EDITORS ;
  miCloseFile->Enabled = Editor != NULL ;
  miCloseAllFiles->Enabled = EditorCount > 0 ;
  miSave->Enabled = Editor != NULL ;
  miSaveAs->Enabled = Editor != NULL ;
  miSaveAll->Enabled = EditorCount > 0 ;
  miPrint->Enabled = Editor != NULL ;
  miUndo->Enabled = Editor != NULL && Editor->CanUndo ;
  miRedo->Enabled = Editor != NULL && Editor->CanRedo ;
  miCut->Enabled = canCut ;
  miDelete->Enabled = canCut ;
  miCopy->Enabled = Editor != NULL && Editor->CanCopy ;
  miPaste->Enabled = Editor != NULL && Editor->CanPaste ;
  EnableSubmenuItems (miSelection, canCut) ;
  EnableSubmenuItems (miExtraIndent, canCut) ;
  EnableSubmenuItems (pmiSelection, canCut) ;
  miShowWhiteSpace->Checked = Editor != NULL && Editor->WhiteSpaceDisplay ;
  miUndoAfterSave->Checked = UndoAfterSave ;
  miCreateBackups->Checked = CreateBackups ;
  miShowMessages->Caption = EditDragPanelVisible ? "Hide Message &Window" : "Show Message &Window" ;
  pmiOpenFile->Enabled = EditorCount < MAX_EDITORS ;
  pmiUndo->Enabled = Editor != NULL && Editor->CanUndo ;
  pmiRedo->Enabled = Editor != NULL && Editor->CanRedo ;
  pmiCut->Enabled = canCut ;
  pmiCopy->Enabled = Editor != NULL && Editor->CanCopy ;
  pmiPaste->Enabled = Editor != NULL && Editor->CanPaste ;
  pmiShowMessages->Caption = EditDragPanelVisible ? "Hide &Messages" : "Show &Messages" ;
  SetSubMenuSelection (miAutoReload, AutoReload) ;
  if (Editor != NULL)
  {
    SetSubMenuSelection (miIndentStyle, (int) Editor->AutoIndent) ;
    SetSubMenuSelection (miScrollBars, (int) Editor->ScrollBars) ;
  }
  miPlayMacro1->Enabled = TCodeMax::GetMacro (0, NULL) > 0 ;
  miPlayMacro2->Enabled = TCodeMax::GetMacro (1, NULL) > 0 ;
  miPlayMacro3->Enabled = TCodeMax::GetMacro (2, NULL) > 0 ;
  miPlayMacro4->Enabled = TCodeMax::GetMacro (3, NULL) > 0 ;
  miPlayMacro5->Enabled = TCodeMax::GetMacro (4, NULL) > 0 ;
  miPlayMacro6->Enabled = TCodeMax::GetMacro (5, NULL) > 0 ;
  miPlayMacro7->Enabled = TCodeMax::GetMacro (6, NULL) > 0 ;
  miPlayMacro8->Enabled = TCodeMax::GetMacro (7, NULL) > 0 ;
  miPlayMacro9->Enabled = TCodeMax::GetMacro (8, NULL) > 0 ;
  miPlayMacro10->Enabled = TCodeMax::GetMacro (9, NULL) > 0 ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miUndoClick(TObject *Sender)
{
  if (Editor != NULL)
    Editor->Undo () ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miRedoClick(TObject *Sender)
{
  if (Editor != NULL)
    Editor->Redo () ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miCutClick(TObject *Sender)
{
  if (Editor != NULL)
    Editor->Cut () ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miCopyClick(TObject *Sender)
{
  if (Editor != NULL)
    Editor->Copy () ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miPasteClick(TObject *Sender)
{
  if (Editor != NULL)
    Editor->Paste () ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::MenusPopup(TObject *Sender)
{
  SetMenuState () ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::PopupMenuPopup(TObject *Sender)
{
  int                   line ;
  int                   col ;

  SetMenuState () ;
  if (Editor == NULL || (line = Editor->RMBDownLine) == -1 || (col = Editor->RMBDownCol) == -1)
    return ;
  IncludeFilename = LocateIncludeFilename (line, col, Editor) ;
  if (IncludeFilename != "")
  {
    AnsiString str = IncludeFilename ;
    if (str.Length () > 32)
      str = str.SubString (1, 29) + "..." ;
    pmiOpenFileUnderCursor->Caption = AnsiString ("Open \"") + str + "\"" ;
    pmiOpenFileUnderCursor->Hint = AnsiString ("Attempt to locate and open \"") + IncludeFilename + "\"" ;
    pmiOpenFileUnderCursor->Enabled = true ;
    pmiOpenFileUnderCursor->OnClick = pmiOpenFileUnderCursorClick ;
  }
  else
  {
    CommandLine = LocateCommandLine (line, Editor) ;
    if (CommandLine != "")
    {
      AnsiString str = CommandLine ;
      if (str.Length () > 32)
        str = str.SubString (1, 29) + "..." ;
      pmiOpenFileUnderCursor->Caption = AnsiString ("Cop&y \"") + str + "\" to Command-Line" ;
      pmiOpenFileUnderCursor->Hint = AnsiString ("Copy the string \"") + CommandLine + "\" to the toolbar command-line" ;
      pmiOpenFileUnderCursor->Enabled = true ;
      pmiOpenFileUnderCursor->OnClick = pmiCopyCommandLineClick ;
    }
    else
    {
      pmiOpenFileUnderCursor->Caption = "Open Include File/Insert Command Line" ;
      pmiOpenFileUnderCursor->Hint = "No include file or command-line found" ;
      pmiOpenFileUnderCursor->Enabled = false ;
    }
  }
  if (GetSubMenu (PopupMenu->Handle, 0) != miInsertMenu->Handle)
    InsertMenu (PopupMenu->Handle, 0, MF_BYPOSITION | MF_POPUP, (UINT) miInsertMenu->Handle, "Insert") ;
}

//---------------------------------------------------------------------------
void LocateRecentFile (AnsiString FileName, TCodeMax *e)
{
  AnsiString            str ;

  for (int i = 0 ; i < RecentFiles->Count ; i++)
  {
    str = RecentFiles->Strings [i] ;
    if (FileName.AnsiCompareIC (GetNextField (str)) == 0)
    {
      try
      {
        e->Line = GetNextField (str).ToInt () ;
        e->Col = GetNextField (str).ToInt () ;
        e->TopLine = GetNextField (str).ToInt () ;
        e->Language = (TLanguage) GetNextField (str).ToInt () ;
        e->TabSize = GetNextField (str).ToInt () ;
        e->AutoIndent = (TAutoIndent) GetNextField (str).ToInt () ;
      }
      catch (EConvertError& E)
      {
      }
      return ;
    }
  }
  for (int i = 0 ; i < OlderFiles->Count ; i++)
  {
    str = OlderFiles->Strings [i] ;
    if (FileName.AnsiCompareIC (GetNextField (str)) == 0)
    {
      try
      {
        e->Line = GetNextField (str).ToInt () ;
        e->Col = GetNextField (str).ToInt () ;
        e->TopLine = GetNextField (str).ToInt () ;
        e->Language = (TLanguage) GetNextField (str).ToInt () ;
        e->TabSize = GetNextField (str).ToInt () ;
        e->AutoIndent = (TAutoIndent) GetNextField (str).ToInt () ;
      }
      catch (EConvertError& E)
      {
      }
      return ;
    }
  }
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::RecentMenuClick (TObject *Sender)
{
  TMenuItem             *m = (TMenuItem *) Sender ;
  AnsiString            str ;
  TCodeMax              *e ;

  UpdateRecent () ;
  if (m->Tag >= RecentFiles->Count)
    return ;
  str = RecentFiles->Strings [m->Tag] ;
  AddToRecent (str) ;
  if (SelectFile (str.c_str ()))
    return ;
  if ((e = ParentForm->CreateNewEditor (GetNextField (str).c_str ())) != NULL)
  {
    try
    {
      e->Line = GetNextField (str).ToInt () ;
      e->Col = GetNextField (str).ToInt () ;
      e->TopLine = GetNextField (str).ToInt () ;
      e->Language = (TLanguage) GetNextField (str).ToInt () ;
      e->TabSize = GetNextField (str).ToInt () ;
      e->AutoIndent = (TAutoIndent) GetNextField (str).ToInt () ;
    }
    catch (EConvertError& E)
    {
    }
  }
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::OlderMenuClick (TObject *Sender)
{
  TMenuItem             *m = (TMenuItem *) Sender ;
  AnsiString            str ;
  TCodeMax              *e ;

  UpdateRecent () ;
  if (m->Tag >= OlderFiles->Count)
    return ;
  str = OlderFiles->Strings [m->Tag] ;
  AddToRecent (str) ;
  if (SelectFile (str.c_str ()))
    return ;
  if ((e = ParentForm->CreateNewEditor (GetNextField (str).c_str ())) != NULL)
  {
    try
    {
      e->Line = GetNextField (str).ToInt () ;
      e->Col = GetNextField (str).ToInt () ;
      e->TopLine = GetNextField (str).ToInt () ;
      e->Language = (TLanguage) GetNextField (str).ToInt () ;
      e->TabSize = GetNextField (str).ToInt () ;
      e->AutoIndent = (TAutoIndent) GetNextField (str).ToInt () ;
    }
    catch (EConvertError& E)
    {
    }
  }
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miFindClick(TObject *Sender)
{
  if (Editor != NULL)
  {
    Editor->Find () ;
    ec.WholeWordEnabled = Editor->WholeWordEnabled ;
    ec.PreserveCase = Editor->PreserveCase ;
    ec.CaseSensitive = Editor->CaseSensitive ;
    ec.RegexpEnabled = Editor->RegexpEnabled ;
  }
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miReplaceClick(TObject *Sender)
{
  if (Editor != NULL)
  {
    Editor->Replace () ;
    ec.WholeWordEnabled = Editor->WholeWordEnabled ;
    ec.PreserveCase = Editor->PreserveCase ;
    ec.CaseSensitive = Editor->CaseSensitive ;
    ec.RegexpEnabled = Editor->RegexpEnabled ;
  }
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miFindNextClick(TObject *Sender)
{
  if (Editor != NULL)
    Editor->FindNext () ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miGoToLineClick(TObject *Sender)
{
  if (Editor != NULL)
    Editor->GoToLine (-1) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::pmiContextHelpClick(TObject *Sender)
{
  GetContextHelp () ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::InsertMenuClick (TObject *Sender)
{
  AnsiString            filename ;
  CM_POSITION           position ;

  if (Editor != NULL && ((TMenuItem *) Sender)->Tag != 0)
  {
    filename = *(AnsiString *) ((TMenuItem *) Sender)->Tag ;
    filename += ".txt" ;
    Editor->GetPosition (&position) ;
    if (Editor->InsertFile (filename, &position) == CME_FAILURE)
      ShowErrorMessage ("", "Failed to insert file") ;
  }
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miEditInsertMenuClick(TObject *Sender)
{
  ShellExecute (Handle, "open", InsertPath.c_str (), NULL, NULL, SW_SHOWNORMAL) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::EditDragPanelMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
{
  SetCapture (EditDragPanel->Handle) ;
  EditPanelDrag = true ;
  EditStartDragOffset = EditDragOffset ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::EditDragPanelMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
{
  ReleaseCapture () ;
  EditPanelDrag = false ;
  if (EditDragOffset >= 15)
  {
    EditPanel->Height = EditDragPanel->Top - EditPanel->Top ;
    int Top = EditDragPanel->Top + EditDragPanel->Height ;
    int Height = Tab->Height - Top - TabYBorder ;
    MessagePanel->SetBounds (MessagePanel->Left, Top, TabClientWidth, Height) ;
  }
  else
  {
    EditDragOffset = EditStartDragOffset ;
    EditDragPanel->Top = Tab->Height - EditDragOffset ;
    ShowEditDragPanel (false) ;
    EditDragPanelVisible = false ;
  }
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::FormResize(TObject *Sender)
{
  TabClientWidth = HiddenPanel->Width ;
  EditDragPanel->Width = TabClientWidth ;
  if (EditDragPanelVisible && Editor != NULL)
  {
    if (EditDragOffset < EditDragPanel->Height + 32)
      EditDragOffset = EditDragPanel->Height + 32 ;
    if (Tab->Height - EditDragOffset < TabClientTop + 32)
    {
      EditDragPanel->Top = TabClientTop + 32 ;
      EditDragOffset = Tab->Height - EditDragPanel->Top ;
    }
    else
      EditDragPanel->Top = Tab->Height - EditDragOffset ;
    if (EditDragPanel->Top < 32 || EditDragOffset < EditDragPanel->Height + 32)
    {
      ShowEditDragPanel (false) ;
      EditDragPanelVisible = false ;
      EditDragOffset = 32 ;
      return ;
    }
    int Height = EditDragPanel->Top - EditPanel->Top ;
    EditPanel->SetBounds (EditPanel->Left, EditPanel->Top, TabClientWidth, Height) ;
    int Top = EditDragPanel->Top + EditDragPanel->Height ;
    Height = Tab->Height - Top - TabYBorder ;
    MessagePanel->SetBounds (MessagePanel->Left, Top, TabClientWidth, Height) ;
  }
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::EditDragPanelMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
{
  int         top ;

  if (EditPanelDrag)
  {
    top = EditDragPanel->Top + Y ;
    if (top < EditPanel->Top + 64)
      top = EditPanel->Top + 64 ;
    if (top + EditDragPanel->Height > Tab->Height)
      top = Tab->Height - EditDragPanel->Height ;
    EditDragPanel->Top = top ;
    EditDragOffset = Tab->Height - EditDragPanel->Top ;
    EditDragPanelTimer->Enabled = true ;
  }
}

//---------------------------------------------------------------------------
void TParentForm::ShowEditDragPanel (bool Showing)
{
  if (Showing)
  {
    MessagePanel->Align = alNone ;
    EditPanel->Align = alTop ;
    EditDragPanel->Visible = true ;
    FormResize (this) ;
    EditDragPanel->BringToFront () ;
  }
  else
  {
    MessagePanel->Align = alClient ;
    EditPanel->Align = alClient ;
    EditDragPanel->Visible = false ;
  }
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::pmiShowMessagesClick(TObject *Sender)
{
  EditDragPanelVisible = !EditDragPanelVisible ;
  ShowEditDragPanel (EditDragPanelVisible) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::EditDragPanelTimerTimer(TObject *Sender)
{
  if (EditPanelDrag)
  {
    EditPanel->Height = EditDragPanel->Top - EditPanel->Top ;
    int Top = EditDragPanel->Top + EditDragPanel->Height ;
    int Height = Tab->Height - Top - TabYBorder ;
    MessagePanel->SetBounds (MessagePanel->Left, Top, TabClientWidth, Height) ;
  }
  EditDragPanelTimer->Enabled = false ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miShowParseMessagesClick(TObject *Sender)
{
  miShowParseMessages->Checked = !miShowParseMessages->Checked ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miAutoLoadErrorFileClick(TObject *Sender)
{
  miAutoLoadErrorFile->Checked = !miAutoLoadErrorFile->Checked ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::CodeMaxKeyDown (TObject *Sender, WORD &Key, TShiftState Shift)
{
  int                   value = ShortCut (Key, Shift) ;
  TMenuItem             *m ;

  if (Key == 0x1b)
  {
    if (EditDragPanel->Visible)
    {
      EditDragPanelVisible = false ;
      ShowEditDragPanel (false) ;
      Key = 0 ;
      return ;
    }
  }
  if ((m = Menus->FindItem (value, fkShortCut)) != NULL)
  {
    Key = 0 ;
    m->Click () ;
  }
  else if ((m = PopupMenu->FindItem (value, fkShortCut)) != NULL)
  {
    Key = 0 ;
    m->Click () ;
  }
}

//---------------------------------------------------------------------------
AnsiString HotKeyString (WORD Command)
{
  int                   count ;
  CM_HOTKEY             *hotkeys ;
  CM_HOTKEY             lastKey ;
  CM_HOTKEY             key ;
  AnsiString            str ;

  if ((count = TCodeMax::GetHotKeysForCmd (Command, NULL)) == 0)
    return (str) ;
  hotkeys = new CM_HOTKEY [count] ;
  TCodeMax::GetHotKeysForCmd (Command, hotkeys) ;
  while (count--)
  {
    key = hotkeys [count] ;
    if (str != "")
    {
      bool lastIsChar = lastKey.nVirtKey1 >= '0' && lastKey.nVirtKey1 <= 'Z' ;
      bool thisIsChar = key.nVirtKey1 >= '0' && key.nVirtKey1 <= 'Z' ;
      if (lastIsChar && !thisIsChar)
        continue ;
      if (lastIsChar && thisIsChar)
        if (lastKey.nVirtKey1 < key.nVirtKey1)
          continue ;
    }
    str = TCodeMax::GetHotKeyString (key) ;
    lastKey = key ;
  }
  delete [] hotkeys ;
  return (str) ;
}

//---------------------------------------------------------------------------
void TParentForm::SetMenuShortcut (TMenuItem *m, WORD Command)
{
  int                   pos ;
  AnsiString            hks = HotKeyString (Command) ;

  if ((pos = m->Caption.Pos ("\t")) != 0)
    m->Caption.Delete (pos, 255) ;
  if (hks != "")
    m->Caption = m->Caption + "\t" + hks ;
}

//---------------------------------------------------------------------------
void TParentForm::SetMenuShortcut (TMenuItem *m1, TMenuItem *m2, WORD Command)
{
  int                   pos ;
  AnsiString            hks = HotKeyString (Command) ;

  if ((pos = m1->Caption.Pos ("\t")) != 0)
    m1->Caption.Delete (pos, 255) ;
  if ((pos = m2->Caption.Pos ("\t")) != 0)
    m2->Caption.Delete (pos, 255) ;
  if (hks != "")
  {
    m1->Caption = m1->Caption + "\t" + hks ;
    m2->Caption = m2->Caption + "\t" + hks ;
  }
}

//---------------------------------------------------------------------------


void __fastcall TParentForm::miSelectAllClick(TObject *Sender)
{
  if (Editor != NULL)
    Editor->ExecuteCommand (CMD_SELECTALL, 0) ;    
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miDeleteClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand (CMD_DELETE, 0) ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miIndentSelectionClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand (CMD_INDENTSELECTION, 0) ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miUndentSelectionClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand (CMD_UNINDENTSELECTION, 0) ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miIndentSelectionPreviousClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand (CMD_INDENTTOPREV, 0) ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miUppercaseSelectionClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand (CMD_UPPERCASESELECTION, 0) ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miLowercaseSelectionClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand (CMD_LOWERCASESELECTION, 0) ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miSpacesToTabsClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand (CMD_TABIFYSELECTION, 0) ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miTabsToSpacesClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand (CMD_UNTABIFYSELECTION, 0) ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miMatchBraceClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand (CMD_GOTOMATCHBRACE, 0) ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miSetRepeatCountClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand (CMD_SETREPEATCOUNT, 0) ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miRecordMacroClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand (CMD_RECORDMACRO, 0) ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miPlayMacroClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand ((WORD) (CMD_PLAYMACRO1 + ((TMenuItem *) Sender)->Tag), 0) ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miClearAllBookmarksClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand (CMD_BOOKMARKCLEARALL, 0) ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miFirstBookmarkClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand (CMD_BOOKMARKJUMPTOFIRST, 0) ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miLastBookmarkClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand (CMD_BOOKMARKJUMPTOLAST, 0) ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miNextBookmarkClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand (CMD_BOOKMARKNEXT, 0) ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miPreviousBookmarkClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand (CMD_BOOKMARKPREV, 0) ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miToggleBookmarkClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand (CMD_BOOKMARKTOGGLE, 0) ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miShowWhiteSpaceClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->ExecuteCommand (CMD_TOGGLEWHITESPACEDISPLAY, 0) ;
  miShowWhiteSpace->Checked = Editor->WhiteSpaceDisplay ;
}
//---------------------------------------------------------------------------

void __fastcall TParentForm::miIndentStyleClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  Editor->AutoIndent = ec.AutoIndent = (TAutoIndent) ((TMenuItem *) Sender)->Tag ;
  SetSubMenuSelection (miIndentStyle, (int) ec.AutoIndent) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miSetTabSizeClick(TObject *Sender)
{
  if (Editor == NULL)
    return ;
  GenericInputForm->Caption = "Enter New Tab Size" ;
  GenericInputForm->Value->Increment = 1 ;
  GenericInputForm->Value->MinValue = 1 ;
  GenericInputForm->Value->MaxValue = 16 ;
  GenericInputForm->Value->Value = Editor->TabSize ;
  if (GenericInputForm->ShowModal () == mrOk)
    Editor->TabSize = ec.TabSize = GenericInputForm->Value->Value ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miScrollBarsClick(TObject *Sender)
{
  int                   i ;
  TCodeMax              **e ;

  ec.ScrollBars = (TScrollStyle) ((TMenuItem *) Sender)->Tag ;
  SetSubMenuSelection (miScrollBars, (int) ec.ScrollBars) ;
  for (i = 0, e = Editors ; i < EditorCount ; i++, e++)
  {
    (*e)->ScrollBars = ec.ScrollBars ;
    (*e)->VSplitterEnabled = true ;
    (*e)->HSplitterEnabled = true ;
  }
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miCreateBackupsClick(TObject *Sender)
{
  CreateBackups = !CreateBackups ;
  miCreateBackups->Checked = CreateBackups ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miAutoSaveClick(TObject *Sender)
{
  GenericInputForm->Caption = "Time in minutes (0 to turn off)" ;
  GenericInputForm->Value->MinValue = 0 ;
  GenericInputForm->Value->MaxValue = 60 ;
  GenericInputForm->Value->Value = AutoSaveTimer->Interval / 60000 ;
  if (GenericInputForm->ShowModal () == mrOk)
  {
    AutoSaveTimer->Interval = (int) GenericInputForm->Value->Value * 60000 ;
    AutoSaveTimer->Enabled = AutoSaveTimer->Interval > 0 ;
    AnsiString str ("AutoSave is ") ;
    if (AutoSaveTimer->Enabled)
      str += AnsiString (AutoSaveTimer->Interval / 60000) + " minute(s)" ;
    else
      str += "disabled" ;
    PutStatusMessage (str) ;
  }
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::AutoSaveTimerTimer(TObject *Sender)
{
  AutoSaveTimer->Enabled = false ;
  if (SaveAllFiles (false))
    PutStatusMessage ("File(s) auto-saved") ;
  AutoSaveTimer->Enabled = AutoSaveTimer->Interval > 0 ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miBlackOnWhiteClick(TObject *Sender)
{
  memcpy (&ec.Colours, &DefaultColours, sizeof (CM_COLORS)) ;
  for (int i = 0 ; i < EditorCount ; i++)
    Editors [i]->SetColors (&ec.Colours) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miWhiteOnBlackClick(TObject *Sender)
{
  memcpy (&ec.Colours, &CJCColours, sizeof (CM_COLORS)) ;
  for (int i = 0 ; i < EditorCount ; i++)
    Editors [i]->SetColors (&ec.Colours) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::pmiCopyCommandLineClick(TObject *Sender)
{
  SendMessage (MainWindow, COPY_COMMANDLINE_MESSAGE, 0, (DWORD) CommandLine.c_str ()) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::pmiOpenFileUnderCursorClick(TObject *Sender)
{
  TCodeMax              *e ;
  AnsiString            str ;
  TStringList           *sl ;

  if (Editor == NULL)
    return ;
  str = IncludeFilename ;
  // append dir if it isn't an absolute path
  if (str [1] != '\\' && !(str.Length () > 1 && isalpha (str [1]) && str [2] == ':'))
    str = GetFilePath (Editor->FileName) + "\\" + IncludeFilename ;
  if (FileExists (str))
  {
    if ((e = CreateNewEditor (str.c_str ())) != NULL)
    {
      LocateRecentFile (str, e) ;
      str = str + "," ;
      str += AnsiString (e->Line) + "," ;
      str += AnsiString (e->Col) + "," ;
      str += AnsiString (e->TopLine) + "," ;
      str += AnsiString ((int) e->Language) + "," ;
      str += AnsiString (e->TabSize) + "," ;
      str += AnsiString ((int) e->AutoIndent) ;
      AddToRecent (str) ;
    }
    return ;
  }
  sl = new TStringList ;
  sl->LoadFromFile (POVRayIniPath) ;
  for (int i = 0 ; i < sl->Count ; i++)
  {
    str = sl->Strings [i].Trim () ;
    if (str.SubString (1, 12).AnsiCompareIC (AnsiString ("Library_Path")) == 0)
    {
      str = str.SubString (13, MAX_PATH).TrimLeft () ;
      if (str [1] != '=')
        continue ;
      str.Delete (1, 1) ;
      str = FixPath (UnquotePath (str) + "\\" + IncludeFilename) ;
      // append dir if it isn't an absolute path
      if (str [1] != '\\' && !(str.Length () > 1 && isalpha (str [1]) && str [2] == ':'))
        str = GetFilePath (Editor->FileName) + "\\" + str ;
      if (FileExists (str))
      {
        delete sl ;
        if ((e = CreateNewEditor (str.c_str ())) != NULL)
        {
          LocateRecentFile (str, e) ;
          str = str + "," ;
          str += AnsiString (e->Line) + "," ;
          str += AnsiString (e->Col) + "," ;
          str += AnsiString (e->TopLine) + "," ;
          str += AnsiString ((int) e->Language) + "," ;
          str += AnsiString (e->TabSize) + "," ;
          str += AnsiString ((int) e->AutoIndent) ;
          AddToRecent (str) ;
        }
        return ;
      }
    }
  }
  delete sl ;
  PutStatusMessage ("Could not locate file") ;
}

//---------------------------------------------------------------------------
AnsiString TParentForm::LocateIncludeFilename (int line, int col, TCodeMax *e)
{
  int                   pos ;
  AnsiString            str = "" ;
  AnsiString            name ;

  if (line == -1 || col == -1)
    return (str) ;
  str = e->Lines [line] ;
  if (str.Length () == 0)
    return (str) ;
  if (col > str.Length ())
    col = str.Length () ;
  if ((pos = str.Pos ("\"")) >= col)
    col = pos + 1 ;
  for (pos = col ; pos <= str.Length () ; pos++)
    if (str [pos] == '"')
      break ;
  if (pos > str.Length ())
    return (AnsiString ("")) ;
  str = str.SubString (1, pos - 1) ;
  for (pos = pos - 1 ; pos > 0 ; pos--)
    if (str [pos] == '"')
      break ;
  if (pos == 0)
    return (AnsiString ("")) ;
  return (str.SubString (pos + 1, MAX_PATH)) ;
}

//---------------------------------------------------------------------------
AnsiString TParentForm::LocateCommandLine (int line, TCodeMax *e)
{
  int                   pos ;
  char                  *s ;
  AnsiString            str ;

  if (line == -1)
    return ("") ;
  str = e->Lines [line] ;
  if (str.Length () < 3)
    return ("") ;
  if ((pos = str.Pos ("//")) == 0)
    return ("") ;
  str.Delete (1, pos + 1) ;
  str = str.Trim () ;
  if ((pos = str.LowerCase ().Pos ("cmd:")) != 0)
  {
    str.Delete (1, pos + 3) ;
    return (str.Trim ()) ;
  }
  s = str.c_str () ;
  if (*s != '+' && *s != '-')
    return ("") ;
  if (!isalpha (s [1]))
    return ("") ;
  return (str) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miAutoReloadAlwaysClick(TObject *Sender)
{
  AutoReload = ((TMenuItem *) Sender)->Tag ;
  SetSubMenuSelection (miAutoReload, AutoReload) ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miCursorBeyondEOLClick(TObject *Sender)
{
  miCursorBeyondEOL->Checked = !miCursorBeyondEOL->Checked ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miConstrainCaretClick(TObject *Sender)
{
  miConstrainCaret->Checked = !miConstrainCaret->Checked ;
  ec.SelBoundsEnabled = miConstrainCaret->Checked ;
  for (int i = 0 ; i < EditorCount ; i++)
    Editors [i]->SelBoundsEnabled = ec.SelBoundsEnabled ;
  if (miConstrainCaret->Checked)
  {
    miCursorBeyondEOL->Checked = false ;
    miCursorBeyondEOL->Enabled = false ;
  }
  else
    miCursorBeyondEOL->Enabled = true ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miOverlayKeywordExpansionClick (TObject *Sender)
{
  miOverlayKeywordExpansion->Checked = !miOverlayKeywordExpansion->Checked ;
  ec.TabKeywordExpansion = miOverlayKeywordExpansion->Checked ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miFileMenuHelpClick (TObject *Sender)
{
  SendMessage (MainWindow, KEYWORD_LOOKUP_MESSAGE, 0, (DWORD) "File Menu") ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miEditMenuHelpClick (TObject *Sender)
{
  SendMessage (MainWindow, KEYWORD_LOOKUP_MESSAGE, 0, (DWORD) "Edit Menu") ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miSearchMenuHelpClick (TObject *Sender)
{
  SendMessage (MainWindow, KEYWORD_LOOKUP_MESSAGE, 0, (DWORD) "Search Menu") ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miTextMenuHelpClick (TObject *Sender)
{
  SendMessage (MainWindow, KEYWORD_LOOKUP_MESSAGE, 0, (DWORD) "Text Menu") ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miEditorMenuHelpClick (TObject *Sender)
{
  SendMessage (MainWindow, KEYWORD_LOOKUP_MESSAGE, 0, (DWORD) "Editor Menu") ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::miInsertMenuHelpClick (TObject *Sender)
{
  SendMessage (MainWindow, KEYWORD_LOOKUP_MESSAGE, 0, (DWORD) "Insert Menu") ;
}

//---------------------------------------------------------------------------
void __fastcall TParentForm::FormDestroy(TObject *Sender)
{
  // have to do this otherwise process can hang with 100% CPU during exit on
  // NT4. The problem us during the destruction of the popup menu; if the
  // insert menu is part of it, th VCL code attempts to destroy it by doing
  // this ...
  //   while GetMenuItemCount(Handle) > 0 do RemoveMenu(Handle, 0, MF_BYPOSITION);
  // unfortunately they do not check the return value of RemoveMenu, which in this
  // case is 0 (meaning it has failed). hence it loops forever.
  //
  // I wasn't able to get at the last error value so I don't know why it was
  // failing on NT4 but not on W2K or W95/98 etc. It may have something to do
  // with menu ownership or threads or whatnot.

  DeleteMenu (PopupMenu->Handle, 0, MF_BYPOSITION) ;
}

//---------------------------------------------------------------------------

